핫 감자 세일즈맨


23

포인트 목록이 주어지면 모든 포인트를 방문하여 시작점으로 돌아 오는 최단 경로를 찾으십시오.

여행 세일즈맨 문제 로 / 계산을 근사하는 방법에는 여러 가지가 있으며, 컴퓨터 과학 분야에서 잘 알려져있다. 매우 큰 포인트 그룹에 대해 해결되었지만 가장 큰 일부는 완료하는 데 많은 CPU 년이 걸립니다.

감자에 타지 마십시오.

Hot Potato 는 음악을 재생하는 동안 2 명 이상의 플레이어가 원 안에 "감자"를 통과하는 게임입니다. 목표는 다음 플레이어에게 빠르게 전달하는 것입니다. 음악이 멈췄을 때 감자를 들고 있다면 밖에 있습니다.


의 목적 뜨거운 감자 세일즈맨 이다 :

100 개의 고유 한 점 세트가 주어지면 해당 점을 더 나은 순서로 반환합니다 ( 아래에서 정의 된 총 거리가 짧음 ). 이것은 다음 플레이어에게 문제를 "전달"합니다. 그들은 그것을 향상시키고 다음에 전달해야합니다. 만약 어떤 선수가 그것을 개선 할 수 없다면, 그들은 한 플레이어가 남을 때까지 나가고 게임을 계속합니다.

이것이 "경로의 힘 (brute-force-me-a-a-path)"경쟁이되지 않도록하기 위해 다음과 같은 규정이 있습니다.

  • 감자를 통과하는 데 1 분 이상 걸릴 수 없습니다 . 1 분이 지날 때까지 더 짧은 솔루션을 찾지 못하고 지나갔다면 나갈 것입니다.

  • 25 포인트 이상의 위치는 변경할 수 없습니다 . 정확하게, >= 75포인트는받은 포인트와 같은 위치에 있어야합니다. 중요하지 않습니다 어떤 당신이 변화에 바로 결정 것들 금액 변경합니다.

한 명의 플레이어 만 남으면 해당 게임의 승자가되고 1 점을받습니다. 토너먼트는 5*n게임으로 구성되며 n플레이어 수는 어디 입니까 ? 각 게임, 시작 플레이어가 회전 하고 나머지 플레이어 순서가 입니다. 마지막에 가장 많은 점수를 얻은 플레이어가 토너먼트의 승자입니다. 토너먼트가 1 위로 동점 인 경우 해당 참가자 만 새로운 토너먼트를 진행합니다. 동점이 없을 때까지 계속됩니다.

각 게임의 시작 플레이어는 의사 순서대로 선택된 무작위 의사 점수를받습니다.

포인트는 x,y데카르트 그리드에서 정수 좌표 쌍으로 정의됩니다 . 거리가 측정되는 맨하탄 거리 , |x1-x2| + |y1-y2|. 모든 좌표가 [0..199]범위 내에 있습니다.

입력

입력은 단일 문자열 인수로 제공됩니다. 현재 플레이어 수 ( m)와 100 점을 나타내는 201 개의 쉼표로 구분 된 정수로 구성됩니다 .

m,x0,y0,x1,y1,x2,y2,...,x99,y99

이 지점의 순서는 현재 경로입니다. 총 거리는 각 점에서 다음 점까지의 거리를 더하여 구합니다 ( dist(0,1) + dist(1,2) + ... + dist(99,0)).총 거리를 계산할 때 시작으로 돌아가는 것을 잊지 마십시오!

그 주 m입니다 하지 게임을 시작 플레이어의 수, 그것은 여전히 수입니다.

산출

출력은 입력과 동일한 방식으로 마이너스 m; 새로운 순서로 포인트를 나타내는 쉼표로 구분 된 정수를 포함하는 단일 문자열

x0,y0,x1,y1,x2,y2,...,x99,y99

제어 프로그램은 1 분 동안 만 출력을 기다립니다. 출력이 수신되면 다음을 확인합니다.

  • 출력이 잘 구성되어 있습니다
  • 출력은 다음과 같이 구성 하고 모든 100 점의 입력에 존재
  • >=75 포인트는 원래 위치에 있습니다
  • 경로 길이가 이전 경로보다 짧습니다.

이러한 검사 중 하나라도 실패하면 (또는 출력이없는 경우) 게임이 종료되고 다음 플레이어로 진행됩니다.

제어 프로그램

이 링크에서 제어 프로그램 찾을 수 있습니다 . 제어 프로그램 자체는 결정 론적이며 다음과 같은 더미 시드로 게시됩니다.1 . 스코어링 중에 사용 된 시드는 다를 수 있으므로, 뱉어 낸 턴 순서 / 포인트 목록을 분석하려고하지 않아도됩니다.

주요 수업은 Tourney입니다. 이것을 실행하면 참가자는 논쟁으로 주어진 전체 토너먼트를 할 것입니다. 각 게임의 승자와 마지막에 탈리를 뱉어냅니다. SwapBots가 2 개인 샘플 토너먼트는 다음과 같습니다.

Starting tournament with seed 1

(0) SwapBot wins a game! Current score: 1
(1) SwapBot wins a game! Current score: 1
(1) SwapBot wins a game! Current score: 2
(1) SwapBot wins a game! Current score: 3
(0) SwapBot wins a game! Current score: 2
(1) SwapBot wins a game! Current score: 4
(1) SwapBot wins a game! Current score: 5
(1) SwapBot wins a game! Current score: 6
(1) SwapBot wins a game! Current score: 7
(1) SwapBot wins a game! Current score: 8

Final Results:

Wins        Contestant
2       (0) SwapBot
8       (1) SwapBot

한 번에 하나의 게임 만 테스트하려면 Game수업을 대신 실행할 수 있습니다 . 이것은 인수로 주어진 순서대로 플레이어와 하나의 게임을 실행합니다. 기본적으로 현재 플레이어와 경로 길이를 보여주는 재생 별 인쇄도합니다.

또한 포함 된 몇 가지 테스트 선수는 다음과 같습니다 SwapBot, BlockPermuter그리고 TwoSwapBot. 처음 두 개는 점수 매기기에 포함되지 않으므로 테스트 중에 자유롭게 사용하고 남용하십시오. TwoSwapBot 의지 판단에 포함 된 그는 그래서 당신의 A-게임을 가지고, 능력있는 없습니다 수.

잡록

  • 상태 정보를 저장할 수 없으며 각 차례는 프로그램의 개별 실행입니다. 매 차례마다 받게 될 유일한 정보는 포인트 세트입니다.

  • 외부 리소스를 사용할 수 없습니다. 여기에는 네트워크 호출 및 파일 액세스가 포함됩니다.

  • TSP 문제점 또는 변형을 해결 / 지원하도록 설계된 라이브러리 기능을 사용할 수 없습니다.

  • 어떤 식 으로든 다른 플레이어를 조작하거나 방해 할 수 없습니다.

  • 제어 프로그램이나 포함 된 클래스 또는 파일을 어떤 식 으로든 조작하거나 방해 할 수 없습니다.

  • 멀티 스레딩이 허용됩니다.

  • 사용자 당 하나의 제출. 둘 이상의 항목을 제출하면 제출 된 첫 번째 항목 만 입력합니다. 제출물을 변경하려면 원본을 편집 / 삭제하십시오.

  • 토너먼트는 i7-3770K CPU 및 16GB RAM이 장착 된 컴퓨터에서 Ubuntu 13.04에서 실행됩니다 . VM에서 실행되지 않습니다. 내가 악의적 인 것으로 인식하는 것은 귀하가 제출 한 현재 향후 출품작을 즉시 실격시킵니다 .

  • 모든 항목은 명령 줄에서 무료 ( 맥주에서같이 ) 소프트웨어 실행할 수 있어야합니다 . 항목을 컴파일 / 실행하는 데 문제가있는 경우 의견에 도움을 요청합니다. 응답하지 않거나 궁극적으로 실행할 수없는 경우 실격 처리됩니다.

결과 (2014 년 5 월 22 일)

새로운 결과가 나왔습니다! UntangleBot는 경쟁을 상당히 이겼습니다. TwoSwapBot은 7 승을 거두었으며 SANNbot도 승리를 거두었습니다. 다음은 스코어 보드와 원시 출력에 대한 링크입니다 .

Wins        Contestant
22      (2) ./UntangleBot
7       (0) TwoSwapBot
1       (5) SANNbot.R
0       (1) BozoBot
0       (3) Threader
0       (4) DivideAndConquer

약자로 지금 , UntangleBot는 체크 표시를 수상했다. 더 많은 참가자가 나타날 때 토너먼트를 진행하고 이에 따라 허용되는 답변을 변경하기 때문에 참가를 방해하지 마십시오.


댓글 제거 유실 된 정보가 있으면 알려주십시오.
Doorknob

왜 최종 시험 중에 (마지막으로 학교 예고를 통해)이 도전을해야했는지에 대한 이유는 무엇입니까? 당신은 확실히 나쁜 플래너 Geobits입니다.) 그 당시에는 엄청나게 많은 질문이 있었고 지금은 전혀 없습니다 (한 번에 하나만 있으면 더 잘 작동 할 수 있습니다, 힌트) 힌트) ...
Herjan

@Herjan 현재 챔피언을 태클하려고 노력하십시오. 새로운 참가자가 나타나는 대회가 아닌, 그래서 나는 다시 토너먼트를 실행하겠습니다 이상 또는 아무것도. SirDarius를 이기면 그 사람이나 다른 사람이 당신을 이길 수 있습니다. 삶을 다시 호흡하십시오.)
Geobits

@Herjan 항목을 제출하십시오! 여기에 개선의 여지가 많이 있다고 생각합니다. 내 것을 포함한 여기의 대부분의 솔루션은이 문제와 관련된 영리한 알고리즘에 의존하지 않습니다.
SirDarius

수정 제한에 문제가 있다고 생각합니다. 때때로 더 나은 솔루션을 얻으려면 전체 데이터 세트를 수정해야합니다.
Ilya Gazman

답변:


8

UntangleBot (이전의 NiceBot)

두 가지 전략을 사용하는 C ++ 11 봇.
처음에는 25 포인트보다 더 가까운 경로 사이의 교차점을 감지하여 가능한 경우 경로를 "엉 키게"하려고 시도합니다 (얽힘을 풀면 그 사이의 모든 점이 수정되므로).
첫 번째 전략이 실패하면 더 나은 경로를 찾을 때까지 더 나은 거리를 찾기 위해 점을 무작위로 교환합니다.

이 봇은 테스트 토너먼트에서 한 번의 손실로 대략 5 번의 승리로 TwoSwapBot을 지속적으로 이깁니다.

// g++ -std=c++11 -O3 -o UntangleBot UntangleBot.cpp
#include <algorithm>
#include <chrono>
#include <cmath>
#include <iostream>
#include <iterator>
#include <random>
#include <set>
#include <sstream>

const int NPOINTS = 100;

struct Point {
    int x, y;

    Point():x(0),y(0) {}    
    Point(int x, int y):x(x),y(y) {}

    int distance_to(const Point & pt) const {
        return std::abs(x - pt.x) + std::abs(y - pt.y);
    }
};

std::ostream & operator<<(std::ostream & out, const Point & point) {
    return out << point.x << ',' << point.y;
}

int total_distance(const Point points[NPOINTS]) {
    int distance = 0;
    for (int i = 0; i < NPOINTS; ++i) {
        distance += points[i].distance_to(points[(i+1)%NPOINTS]);
    }
    return distance;
}

bool intersects(const Point & p1, const Point & p2, const Point & p3, const Point & p4) {
    double s1_x, s1_y, s2_x, s2_y;
    s1_x = p2.x - p1.x;
    s1_y = p2.y - p1.y;
    s2_x = p4.x - p3.x;
    s2_y = p4.y - p3.y;

    double s, t;
    s = (-s1_y * (p1.x - p3.x) + s1_x * (p1.y - p3.y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p1.y - p3.y) - s2_y * (p1.x - p3.x)) / (-s2_x * s1_y + s1_x * s2_y);

    return s >= 0 && s <= 1 && t >= 0 && t <= 1;
}

int main(int argc, char ** argv) {
    Point points[NPOINTS];

    using Clock = std::chrono::system_clock;
    const Clock::time_point start_time = Clock::now();

    // initialize points
    if (argc < 2) {
        std::cerr << "Point list is missing" << std::endl;
        return 1;
    }
    std::stringstream in(argv[1]);
    int players;
    char v;
    in >> players >> v;
    for (int i = 0; i < NPOINTS; ++i) {
        in >> points[i].x >> v >> points[i].y >> v;
    }

    int original_distance = total_distance(points);

    // detect intersection between any 4 points
    for (int i = 0; i < NPOINTS; ++i) {
        for (int j = i+1; j < NPOINTS; ++j) {
            Point & p1 = points[i];
            Point & p2 = points[(i+1)%NPOINTS];
            Point & p3 = points[j];
            Point & p4 = points[(j+1)%NPOINTS];

            // points must all be distinct
            if (p1.distance_to(p3) == 0 || p1.distance_to(p4) == 0 || p2.distance_to(p3) == 0 || p2.distance_to(p4) == 0) {
                continue;
            }

            // do they intersect ?
            if (!intersects(p1, p2, p3, p4)) {
                continue;
            }

            // can modify less than 25 points ?
            if (std::abs(j-i) > 25) {
                continue;
            }

            // swap points
            for (int m = 0; m < std::abs(j-i)/2; ++m) {
                if (i+1+m != j-m) {
                    std::swap(points[i+1+m], points[j-m]);
                    //std::cerr << "untangle " << i+1+m << " <=> " << j-m << '\n';
                }
            }

            int new_distance = total_distance(points);
            if (new_distance < original_distance) {
                std::copy(std::begin(points), std::end(points)-1, std::ostream_iterator<Point>(std::cout, ","));
                std::cout << points[NPOINTS-1];
                return 0;
            }
            else {
                // swap points back
                for (int m = 0; m < std::abs(j-i)/2; m++) {
                    if (i+1+m != j-m) {
                        std::swap(points[i+1+m], points[j-m]);
                    }
                }
            }
        }
    }

    // more traditional approach if the first fails
    std::mt19937 rng(std::chrono::duration_cast<std::chrono::seconds>(start_time.time_since_epoch()).count());
    std::uniform_int_distribution<> distr(0, NPOINTS-1);
    while (true) {
        // try all possible swaps from a random permutation
        int p1 = distr(rng);
        int p2 = distr(rng);
        std::swap(points[p1], points[p2]);

        for (int i = 0; i < NPOINTS; ++i) {
            for (int j = i+1; j < NPOINTS; ++j) {
                std::swap(points[i], points[j]);
                if (total_distance(points) < original_distance) {
                    std::copy(std::begin(points), std::end(points)-1, std::ostream_iterator<Point>(std::cout, ","));
                    std::cout << points[NPOINTS-1];
                    return 0;
                }
                else {
                    std::swap(points[i], points[j]);
                }
            }
        }

        // they didn't yield a shorter path, swap the points back and try again
        std::swap(points[p1], points[p2]);
    }
    return 0;
}

당신은 19 분 동안 나를 이겼습니다!
Rainbolt

오늘의 결과를 보면 더 많은 투표가 필요합니다.
Geobits

@Geobits 나는 내가 생각해 낸 가장 간단한 일이 여전히 잘하는 것에 놀랐다. 더 도전적인 참가자가되기를 희망합니다!
SirDarius

@SirDarius 좋아, . 가지고 도전의 비트.
Geobits

4

SANNbot

( R 에서 시뮬레이션 된 어닐링 봇 )

로 호출해야합니다 Rscript SANNbot.R.

input <- strsplit(commandArgs(TRUE),split=",")[[1]]
n <- as.integer(input[1])                            # Number of players
init_s <- s <- matrix(as.integer(input[-1]),ncol=2,byrow=TRUE) # Sequence of points
totdist <- function(s){                              # Distance function
    d <- 0
    for(i in 1:99){d <- d + (abs(s[i,1]-s[i+1,1])+abs(s[i,2]-s[i+1,2]))}
    d
    }
gr <- function(s){                                   # Permutation function
    changepoints <- sample(1:100,size=2, replace=FALSE)
    tmp <- s[changepoints[1],]
    s[changepoints[1],] <- s[changepoints[2],]
    s[changepoints[2],] <- tmp
    s
    }
d <- init_d <- totdist(s)                            # Initial distance
k <- 1                                               # Number of iterations
v <- 0
t <- n*(init_d/12000)                                 # Temperature
repeat{
    si <- gr(s)                                      # New sequence
    di <- totdist(si)                                # New distance
    dd <- di - d                                     # Difference of distances
    if(dd <= 0 | runif(1) < exp(-dd/t)){             # Allow small uphill changes
        d <- di
        s <- si
        v <- v+2
        }
    k <- k+1
    if(k > 10000){break}
    if(d > init_d & v>20){s <- init_s; d <- init_d; v <- 0}
    if(v>23){break}
}
cat(paste(apply(s,1,paste,collapse=","),collapse=","))

아이디어는 비교적 간단합니다. 각 턴은 게임에있는 플레이어 수를 "온도"(12000 이상의 현재 거리, 즉 대략 초기 거리로 수정)로 시뮬레이션 한 어닐링 의 "냉각 단계"중 하나 입니다. 적절한 모의 어닐링과의 유일한 차이점은 25 개 이상의 요소를 순열시키지 않는지 확인하고 20 개의 움직임을 모두 사용했지만 결과 시퀀스는 초기보다 가치가 있으며 다시 시작한다는 것입니다.


@Geobits 아 죄송합니다 init_s가 우연히 정의 된 라인을 삭제했습니다 (잘 "사고": 나는 그 라인을 보았고 "왜 또 왔습니까?"라고 생각하고 삭제했습니다 :)). 수정했습니다.
plannapus

프로그램을 사용하여 시도했지만 java Tourney "java Swapbot" "Rscript SANNbot.R"작동하는 것 같습니다.
plannapus

예, 지금 작동하는 것 같습니다.
Geobits 2014 년

이론 상으로는 (내가 완전히 실수하지 않은 경우) 더 많은 플레이어가 게임에 참여할 때 더 잘 수행됩니다.
plannapus

현재이 프로그램은 내 테스트에서 "너무 많은 포인트가 변경 되었기 때문에"조기 종료됩니다. 나는 범프 경우 u체크 제한을,이 발생하지 않습니다 (그것은 매우 잘 수행). 코드가 매우 간단 해 보이지만 R의 단점을 알지 못하므로 논리가 잘못되었는지 알 수 없습니다. (최신 버전의 컨트롤러는 봇을 실행할 때 봇이 나가는 이유에 대한 메시지를 표시 Game하므로 문제를 정확히 찾아 낼 수 있습니다)
Geobits

4

보조 봇

보다 나은 경로를 찾기 위해 Bozosort 의 복잡한 논리를 활용합니다 . 이것처럼 보입니다.

  • 랜덤 포인트 스왑
  • 개선하면
    • 답을 반환
  • 그렇지 않으면
    • 다시 시도하십시오

BozoBot은 이제 멀티 스레딩 으로 향상되었습니다 ! 4 명의 미니언은 이제 목표가 개선 될 때까지 목표를 저글링합니다. 솔루션을 찾는 첫 번째 쿠키를 얻습니다!

분명히 멀티 스레딩에 실패합니다.

import java.util.Random;
public class BozoBot {
    public static String[] input;
    public static void main(String[] args) {
        input = args;
        for(int i = 0; i < 4; i++) new Minion().run();
    }
}

class Minion implements Runnable {
    public static boolean completed = false;
    public static synchronized void output(int[] x, int[] y) {
        if(!completed) {
            completed = true;
            String result = x[0]+","+y[0];
            for (int i = 1; i < 100; i++)
                result+=","+x[i]+","+y[i];
            System.out.print(result);
            // receiveCookie(); // Commented out to save money
        }
    }
    public void run() {
        String[] args = BozoBot.input[0].split(",");
        int[] x = new int[100];
        int[] y = new int[100];
        for (int i = 1; i < 201; i+=2) {
            x[(i-1)/2] = Integer.parseInt(args[i]);
            y[i/2] = Integer.parseInt(args[i+1]);
        }
        int startDistance = 0;
        for (int i = 1; i < 100; i++)
            startDistance += Math.abs(x[i-1]-x[i]) + Math.abs(y[i-1]-y[i]);
        int r1, r2, r3, r4, tX, tY, newDistance;
        Random gen = new java.util.Random();
        while (true) {
            r1 = gen.nextInt(100);
            r2 = gen.nextInt(100);
            r3 = gen.nextInt(100);
            r4 = gen.nextInt(100);
            tX = x[r1];
            x[r1] = x[r2];
            x[r2] = tX;
            tY = y[r1];
            y[r1] = y[r2];
            y[r2] = tY;
            tX = x[r3];
            x[r3] = x[r4];
            x[r4] = tX;
            tY = y[r3];
            y[r3] = y[r4];
            y[r4] = tY;
            newDistance = 0;
            for (int i=1; i < 100; i++)
                newDistance += Math.abs(x[i-1]-x[i]) + Math.abs(y[i-1]-y[i]);
            if(newDistance < startDistance)
                break;
            tX = x[r1];
            x[r1] = x[r2];
            x[r2] = tX;
            tY = y[r1];
            y[r1] = y[r2];
            y[r2] = tY;
            tX = x[r3];
            x[r3] = x[r4];
            x[r4] = tX;
            tY = y[r3];
            y[r3] = y[r4];
            y[r4] = tY;
        }
        output(x,y);
    }
}

Ehhmm ... run ()을 호출하는 대신 미니언을 스레드에 넣지 않아야합니까? AFAIK 이것은 멀티 스레딩이 아닙니다 ...
CommonGuy

@Manu 당신은 나를 붙 잡았다! 나는 그것을 스스로 배우려고 노력했지만 실패했습니다. 어떤 포인터?
Rainbolt

그렇게 생각합니다new Thread(new Minion()).start()
CommonGuy

1
@Manu 감사합니다. 분명히 코딩 할 때이 자습서의 절반 만 읽었습니다 .
Rainbolt

3

TwoSwapBot

로 업그레이드하면 SwapBot이 녀석은 모든 스왑 쌍을 확인합니다. 먼저, 단일 스왑이 경로를 단축시키는 지 확인합니다. 그렇다면 즉시 반환합니다. 그렇지 않은 경우 각 스왑에서 다른 스왑이 단축 되는지 확인 합니다. 그렇지 않으면, 그는 단지 죽는다.

경로는 여전히 반 임의이지만 일반적으로 약 100ms 후에 반환됩니다. 그가 각각의 2 스왑 (약 25M)을 점검해야한다면 약 20 초가 걸립니다.

제출 당시, 이것은 테스트 라운드에서 다른 모든 경쟁자를 이겼습니다.

public class TwoSwapBot {

    static int numPoints = 100;

    String run(String input){
        String[] tokens = input.split(",");
        if(tokens.length < numPoints*2)
            return "bad input? nope. no way. bye.";

        Point[] points = new Point[numPoints];  
        for(int i=0;i<numPoints;i++)
            points[i] = new Point(Integer.valueOf(tokens[i*2+1]), Integer.valueOf(tokens[i*2+2]));
        int startDist = totalDistance(points);

        Point[][] swapped = new Point[(numPoints*(numPoints+1))/2][];       
        int idx = 0;
        for(int i=0;i<numPoints;i++){
            for(int j=i+1;j<numPoints;j++){
                Point[] test = copyPoints(points);
                swapPoints(test,i,j);
                int testDist = totalDistance(test);
                if( testDist < startDist)
                    return getPathString(test);
                else
                    swapped[idx++] = test;
            }
        }

        for(int i=0;i<idx;i++){
            for(int k=0;k<numPoints;k++){
                for(int l=k+1;l<numPoints;l++){
                    swapPoints(swapped[i],k,l);
                    if(totalDistance(swapped[i]) < startDist)
                        return getPathString(swapped[i]);
                    swapPoints(swapped[i],k,l);
                }
            }
        }
        return "well damn";
    }

    void swapPoints(Point[] in, int a, int b){
        Point tmp = in[a];
        in[a] = in[b];
        in[b] = tmp;
    }

    String getPathString(Point[] in){
        String path = "";
        for(int i=0;i<numPoints;i++)
            path += in[i].x + "," + in[i].y + ",";
        return path.substring(0,path.length()-1);
    }

    Point[] copyPoints(Point[] in){
        Point[] out = new Point[in.length];
        for(int i=0;i<out.length;i++)
            out[i] = new Point(in[i].x, in[i].y);
        return out;
    }

    static int totalDistance(Point[] in){
        int dist = 0;
        for(int i=0;i<numPoints-1;i++)
            dist += in[i].distance(in[i+1]);
        return dist + in[numPoints-1].distance(in[0]);
    }

    public static void main(String[] args) {
        if(args.length < 1)
            return;
        System.out.print(new TwoSwapBot().run(args[0]));
    }

    class Point{
        final int x; final int y;
        Point(int x, int y){this.x = x; this.y = y;}
        int distance(Point other){return Math.abs(x-other.x) + Math.abs(y-other.y);}
    }
}

2

실 꿰는 도구

이 봇

  1. 100 점 을 25 점 중 4 10 점으로 나눕니다. 10 포인트 포인트
  2. 각 조각에 대한 스레드를 시작합니다
  3. 스레드에서 시작 및 끝점을 고정 된 상태로 배열을 임의로 섞습니다.
  4. 새 어레이의 거리가 더 짧으면 유지하십시오
  5. 59 초 후에 주 스레드는 결과를 수집하여 인쇄합니다.

아이디어는 다른 봇이 논리에 실패 할 수 있도록 경로를 가장 잘 개선하는 것입니다.

import java.util.Arrays;
import java.util.Collections;

public class Threader {
    public static final int THREAD_COUNT = 10;
    public static final int DOT_COUNT = 100;
    private final Dot[] startDots = new Dot[THREAD_COUNT];
    private final Dot[] endDots = new Dot[THREAD_COUNT];
    private final Dot[][] middleDots = new Dot[THREAD_COUNT][DOT_COUNT/THREAD_COUNT-2];
    private final Worker worker[] = new Worker[THREAD_COUNT];
    private final static long TIME = 59000; 

    public static void main(String[] args) {
        Threader threader = new Threader();
        //remove unnecessary player count to make calculation easier
        threader.letWorkersWork(args[0].replaceFirst("^[0-9]{1,3},", "").split(","));
    }

    public void letWorkersWork(String[] args) {
        readArgs(args);
        startWorkers();
        waitForWorkers();
        printOutput();
    }

    private void readArgs(String[] args) {
        final int magigNumber = DOT_COUNT*2/THREAD_COUNT;
        for (int i = 0; i < args.length; i += 2) {
            Dot dot = new Dot(Integer.parseInt(args[i]), Integer.parseInt(args[i + 1]));
            if (i % magigNumber == 0) {
                startDots[i / magigNumber] = dot;
            } else if (i % magigNumber == magigNumber - 2) {
                endDots[i / magigNumber] = dot;
            } else {
                middleDots[i / magigNumber][(i % magigNumber) / 2 - 1] = dot;
            }
        }
    }

    private void startWorkers() {
        for (int i = 0; i < THREAD_COUNT; i++) {
            worker[i] = new Worker(startDots[i], endDots[i], middleDots[i]);
            Thread thread = new Thread(worker[i]);
            thread.setDaemon(true);
            thread.start();
        }
    }

    private void waitForWorkers() {
        try {
            Thread.sleep(TIME);
        } catch (InterruptedException e) {
        }
    }

    private void printOutput() {
        //get results
        Worker.stopWriting = true;
        int workerOfTheYear = 0;
        int bestDiff = 0;
        for (int i = 0; i < THREAD_COUNT; i++) {
            if (worker[i].diff() > bestDiff) {
                bestDiff = worker[i].diff();
                workerOfTheYear = i;
            }
        }
        //build output
        StringBuilder result = new StringBuilder(1000);
        for (int i = 0; i < THREAD_COUNT; i++) {
            result.append(startDots[i]);
            Dot middle[] = middleDots[i];
            if (i == workerOfTheYear) {
                middle = worker[i].bestMiddle;
            }
            for (int j = 0; j < middle.length; j++) {
                result.append(middle[j]);
            }
            result.append(endDots[i]);
        }
        result.replace(0, 1, ""); //replace first comma
        System.out.print(result);
    }

}

class Worker implements Runnable {

    public Dot start;
    public Dot end;
    private Dot[] middle = new Dot[Threader.DOT_COUNT/Threader.THREAD_COUNT-2];
    public Dot[] bestMiddle = new Dot[Threader.DOT_COUNT/Threader.THREAD_COUNT-2];
    public static boolean stopWriting = false;
    private int bestDist = Integer.MAX_VALUE;
    private final int startDist;

    public Worker(Dot start, Dot end, Dot[] middle) {
        this.start = start;
        this.end = end;
        System.arraycopy(middle, 0, this.middle, 0, middle.length);
        System.arraycopy(middle, 0, this.bestMiddle, 0, middle.length);
        startDist = Dot.totalDist(start, middle, end);
    }

    @Override
    public void run() {
        while (true) {
            shuffleArray(middle);
            int newDist = Dot.totalDist(start, middle, end);
            if (!stopWriting && newDist < bestDist) {
                System.arraycopy(middle, 0, bestMiddle, 0, middle.length);
                bestDist = newDist;
            }
        }
    }

    public int diff() {
        return startDist - bestDist;
    }

    private void shuffleArray(Dot[] ar) {
        Collections.shuffle(Arrays.asList(ar));
    }

}

class Dot {

    public final int x;
    public final int y;

    public Dot(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int distTo(Dot other) {
        return Math.abs(x - other.x) + Math.abs(y - other.y);
    }

    public static int totalDist(Dot start, Dot[] dots, Dot end) {
        int distance = end.distTo(start);
        distance += start.distTo(dots[0]);
        distance += end.distTo(dots[dots.length - 1]);
        for (int i = 1; i < dots.length; i++) {
            distance += dots[i].distTo(dots[i - 1]);
        }
        return distance;
    }

    @Override
    public String toString() {
        return "," + x + "," + y;
    }
}

2
참고 : 출력 끝에 줄 바꿈을 제거하기 위해 변경 println했습니다 print. 그렇지 않으면 충돌이 발생합니다.
Geobits

스레드 수를 동적으로 변경 println하고 변경 했습니다 print. 이제 ... 10 개 스레드로 시작
CommonGuy

1

나누고 정복 + 욕심 로봇

참고 :Game Game.parsePath에 다음이 포함 된 코드를 살펴 보았습니다 .

for(int i=0;i<numPoints;i++){
        test[i] = new Point(Integer.valueOf(tokens[i*2]), Integer.valueOf(tokens[i*2+1]));
        if(test[i].equals(currentPath[i]))
            same++;

그러나 catch(NumberFormatException)블록 이 없으므로 플레이어 프로그램이 문자열을 출력 할 때 프로그램이 중단 될 수 있습니다 (프로그램 main방법 의 끝에서 설명한 것처럼 ). 프로그램에서 예외, 스택 추적 등을 출력 할 수 있으므로이 문제를 해결하는 것이 좋습니다. 그렇지 않으면 프로그램을 실행하기 전에 해당 줄을 주석 처리하십시오.

주제로 돌아 가기

(Java에서)이 구현은 점 목록을 목록에서 무작위로 25 개의 청크로 분할합니다. 그런 다음 각 청크의 포인트 사이의 경로를 단축하는 스레드를 만듭니다 (따라서 "분할 및 정복"). 메인 스레드는 다른 스레드를 모니터링하고 시간 제한 내에 가장 짧은 솔루션을 제시합니다. 스레드가 솔루션과 함께 또는 솔루션없이 죽으면 다른 청크에서 다른 스레드를 다시 시작합니다.

각 스레드는 "greedy"알고리즘을 사용합니다.이 알고리즘은 임의의 지점에서 시작하여 가장 가까운 지점으로 이동하여 모든 지점이 적용될 때까지 반복됩니다 (따라서 "greedy").

노트

  • 이것은 전체 1 분 동안 실행됩니다 (프로그램 시작 / 종료 및 JVM 시작에 3 초를주었습니다-다음에 JVM 시작 루틴이 무엇인지 알 수 없습니다 ...)
  • 솔루션을 찾더라도 검색을 계속하고 1 분이 지난 후에는 최상의 솔루션을 제시합니다.
  • 이 구현이 실제로 좋은지 확실하지 않습니다. 나는 그것을 코딩하는 것이 재미있었습니다 :)
  • 많은 것이 무작위이기 때문에 동일한 입력에 대해 동일한 출력을 제공하지 않을 수 있습니다.

컴파일하고 java DivideAndConquer.class실행하기 만하면 됩니다.

public class DivideAndConquer extends Thread {
    static LinkedList<Point> original;
    static Solution best;
    static LinkedList<DivideAndConquer> bots;
    static final Object _lock=new Object();

    public static void main(String[] args){
        if(args.length != 1) {
            System.err.println("Bad input!");
            System.exit(-1);
        }
        // make sure we don't sleep too long... get the start time
        long startTime = System.currentTimeMillis();
        // parse input
        String[] input=args[0].split(",");
        int numPlayers=Integer.parseInt(input[0]);
        original=new LinkedList<Point>();
        for(int i=1;i<input.length;i+=2){
            original.add(new Point(Integer.parseInt(input[i]), Integer.parseInt(input[i+1])));
        }
        // start threads
        bots=new LinkedList<DivideAndConquer>();
        for(int i=0;i<6;i++)
            bots.add(new DivideAndConquer(i));
        // sleep
        try {
            Thread.sleep(57000 - (System.currentTimeMillis() - startTime));
        } catch(Exception e){} // should never happen, so ignore
        // time to collect the results!
        Solution best=getBestSolution();
        if(best!=null){
            best.apply(original);
            String printStr="";
            for(int i=0;i<original.size();i++){
                Point printPoint=original.get(i);
                printStr+=printPoint.x+","+printPoint.y+",";
            }
            printStr=printStr.substring(0, printStr.length()-1);
            System.out.print(printStr);
        } else {
            System.out.println("Hey, I wonder if the tournament program crashes on NumberFormatExceptions... Anyway, we failed");
        }
    }

    // check the distance
    public static int calculateDistance(List<Point> points){
        int distance = 0;
        for(int i=0;i<points.size();i++){
            int next=i+1;
            if(next>=points.size())next=0;
            distance+=points.get(i).distance(points.get(next));
        }
        return distance;
    }

    public static void solutionFound(Solution s){
        // thanks to Java for having thread safety features built in
        synchronized(_lock){
            // thanks to Java again for short-circuit evaluation
            // saves lazy programmers lines of code all the time
            if(best==null || best.distDifference < s.distDifference){
                best=s;
            }
        }
    }

    public static Solution getBestSolution(){
        // make sure we don't accidentally return
        // the best Solution before it's even
        // done constructing
        synchronized(_lock){
            return best;
        }
    }

    List<Point> myPoints;
    int start;
    int length;
    int id;

    public DivideAndConquer(int id){
        super("DivideAndConquer-Processor-"+(id));
        this.id=id;
        myPoints=new LinkedList<Point>();
        start=(int) (Math.random()*75);
        length=25;
        for(int i=start;i<start+length;i++){
            myPoints.add(original.get(i));
        }
        start();
    }

    public void run(){
        // copy yet again so we can delete from it
        List<Point> copied=new LinkedList<Point>(myPoints);
        int originalDistance=calculateDistance(copied);
        // this is our solution list
        List<Point> solution=new LinkedList<Point>();
        int randomIdx=new Random().nextInt(copied.size());
        Point prev=copied.get(randomIdx);
        copied.remove(randomIdx);
        solution.add(prev);
        while(copied.size()>0){
           int idx=-1;
           int len = -1;
           for(int i=0;i<copied.size();i++){
               Point currentPoint=copied.get(i);
               int dist=prev.distance(currentPoint);
               if(len==-1 || dist<len){
                   len=dist;
                   idx=i;
               }
           }
           prev=copied.get(idx);
           copied.remove(idx);
           solution.add(prev);
        }
        // aaaand check our distance
        int finalDistance=calculateDistance(solution);
        if(finalDistance<originalDistance){
            // yes! solution
            Solution aSolution=new Solution(start, length, solution, originalDistance-finalDistance);
            solutionFound(aSolution);
        }
        // start over regardless
        bots.set(id, new DivideAndConquer(id));
    }

    // represents a solution
    static class Solution {
        int start;
        int length;
        int distDifference;
        List<Point> region;
        public Solution(int start, int length, List<Point> region, int distDifference){
            this.region=new LinkedList<Point>(region);
            this.start=start;
            this.length=length;
            this.distDifference=distDifference;
        }
        public void apply(List<Point> points){
            for(int i=0;i<length;i++){
                points.set(i+start, region.get(i));
            }
        }
    }

    // copied your Point class, sorry but it's useful...
    // just added public to each method for aesthetics
    static class Point{
        int x;
        int y;
        Point(int x, int y){
            this.x = x;
            this.y = y;
        }
        Point(Point other){
            this.x = other.x;
            this.y = other.y;
        }
        public boolean equals(Point other){
            if(this.x==other.x && this.y==other.y)
                return true;
            return false;
        }

        public int distance(Point other){
            return Math.abs(x-other.x) + Math.abs(y-other.y);
        }
    }
}

믿을 수 있습니까? 내가 이것을 제출했을 때 SX는 보안 문자를 요구했다 ! 이것은 당신에게 봇으로 만들어 보입니까? 진심이야?
DankMemes 2014 년

1
NFException이 수정되었습니다. 이제 프로그램을 죽이지 않고 플레이어를 죽입니다.
Geobits

기록을 위해, 나는 당신의 " 이봐, 난 궁금해 ... "줄 에 충돌했을 것이라고 생각하지 않는다 . 있는지 확인합니다<200구문 분석을 시도하기 전에 토큰 . 어쨌든 확인하는 것이 좋습니다.
Geobits

@Geobits haha는 몰랐습니다.
DankMemes

참고 : 이것을 컴파일하려면 )19 행 을 추가해야했습니다 . 변화 substrsubstring 38; 메소드 idx에서 무언가로 초기화 하십시오 run().
Geobits
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.