로봇! 이 피클을 모으십시오!


10

나는 약간의 피클에 들어간 것 같습니다. 말 그대로. 나는 바닥에 많은 피클을 떨어 뜨 렸고 이제는 모두 흩어져 있습니다! 그들 모두를 모으도록 도와주십시오 아, 내가 지휘 할 로봇이 많다고 언급 했습니까? (그들은 또한 모든 곳에서 흩어져 있습니다; 나는 물건을 정리하는 것이 정말로 나쁩니다.)

다음과 같은 형식으로 입력해야합니다.

P.......
..1..2..
.......P
........
P3PP...4
.......P

즉, 하나의 여러 줄 ., P(피클), 또는 숫자 (로봇의 ID). (각 줄의 길이가 같고으로 채워져 있다고 가정 할 .수 있습니다.)이 줄을 배열로 입력하거나 STDIN에서 슬러 프하거나 쉼표로 구분 된 한 줄로 읽거나 파일을 읽거나 원하는 작업을 수행 할 수 있습니다. 입력을 받고 싶습니다.

로봇 의 출력 이 가장 높은 n라인 형태 여야합니다 n. (로봇 ID는 항상 1부터 시작합니다.) 각 줄에는 문자 L(왼쪽), R(오른쪽), U(위쪽) 및 D(아래쪽)으로 구성된 로봇의 경로가 포함됩니다 . 예를 들어, 다음은 해당 퍼즐에 대한 출력 예입니다.

LLU
RDR
LRRR
D

또한 될 수 있습니다

LLU RDR LRRR D

또는

["LLU","RDR","LRRR","D"]

또는 솔루션이 무엇인지 알 수있는 한 원하는 형식.

목표는 단계가 가장 적은 최적의 결과를 찾는 것입니다. 걸음 수는 모든 로봇에서 가장 많은 걸음 수로 계산됩니다. 예를 들어, 위의 예에는 4 단계가 있습니다. 여러 솔루션이있을 수 있지만 하나만 출력하면됩니다.

채점 :

  • 프로그램은 5 가지 (임의로 생성 된) 테스트 사례마다 실행됩니다.
  • 각 런에서 단계를 추가해야하며 이는 점수입니다.
  • 가장 낮은 총 누적 점수가 이깁니다.
  • 이러한 특정 입력에 대해서는 하드 코딩 할 수 없습니다. 다른 입력에도 코드가 작동해야합니다.
  • 로봇 서로 통과 할 수 있습니다 .
  • 프로그램은 결정 론적이어야합니다. 즉, 매 실행마다 동일한 출력입니다. 시드되고 일관되게 동일한 숫자의 크로스 플랫폼을 생성하는 한 난수 생성기를 사용할 수 있습니다.
  • 각 입력에 대해 3 분 이내에 코드를 실행해야합니다. (바람직하게는 훨씬 적습니다.)
  • 동점 인 경우 대부분의 공감대가 승리합니다.

테스트 사례는 다음과 같습니다. 내가 작성한 작은 Ruby 스크립트로 무작위로 생성되었습니다.

P.......1.
..........
P.....P...
..P.......
....P2....
...P.P....
.PP..P....
....P....P
PPPP....3.
.P..P.P..P

....P.....
P....1....
.P.....PP.
.PP....PP.
.2.P.P....
..P....P..
.P........
.....P.P..
P.....P...
.3.P.P....

..P..P..P.
..1....P.P
..........
.......2P.
...P....P3
.P...PP..P
.......P.P
..P..P..PP
..P.4P..P.
.......P..

..P...P...
.....P....
PPPP...P..
..P.......
...P......
.......P.1
.P..P....P
P2PP......
.P..P.....
..........

......PP.P
.P1..P.P..
......PP..
P..P....2.
.P.P3.....
....4..P..
.......PP.
..P5......
P.....P...
....PPP..P

행운을 빕니다. 그리고 피클을 너무 오래 두지 마십시오. 그렇지 않으면 망칠 것입니다!


아, 왜 피클, 물어?

왜 안돼?


3
실제로 "최적의 결과물"을 찾았다는 것을 보여주는 합리적인 방법은 없습니다. 이것은 본질적으로 여행하는 판매원 문제이며 NP가 완료 되었기 때문입니다.
Wally

@ 월리 흠? 아마도 누군가가 제공된 테스트 사례에 대한 최소 단계를 찾아야하며 모든 답변은 그에 근거 할 수 있습니다.
Doorknob

2
테스트 케이스는 아마도 누군가가 그것을 원한다면 최소한의 힘을 가할 정도로 작을 것입니다. 그리고 / 또는 답변을 한 모든 사람은 테스트 사례에 대해 무엇을 받았는지 알 수 있으며 최소한 최소 한도에 맞는 다른 답변을 요구할 수 있습니다.
Wally

3
로봇이 서로를 통과 할 수 있습니까? 그렇지 않은 경우 경로 해석에 대한 타이밍 제한은 무엇입니까?
피터 테일러

1
@Gareth 문제는 테스트 케이스가 공개 될 때까지 점수를 알 수 없으며 그 이후에 제출하면 이미 테스트 케이스를 보게됩니다.
Doorknob

답변:


6

루비, 15 + 26 + 17 + 26 + 17 = 101

로봇이 피클을 찾습니다!

다음은 다음과 같은 초 고순도 알고리즘을 사용하여 사람들을 시작하는 기준입니다.

  • 각 진드기와 각 로봇은 다음 단계를 수행하여 숫자 순서대로 작동합니다.
    • 다른 로봇이 목표로하지 않는 가장 가까운 (맨해튼 거리) 피클을 식별하십시오.
    • 이동할 수있는 인접한 사각형을 알아냅니다.
    • 선택한 피클에 더 가깝게 이동하는 방향을 선호하여 해당 사각형 중 하나를 선택하십시오.

테스트 사례 # 1의 모습은 다음과 같습니다.

TC1의 애니메이션 예제

분명히 이것은 좋지는 않지만 시작입니다!

암호:

Tile = Struct.new(:world, :tile, :x, :y) do
    def passable?
        tile =~ /\.|P/
    end

    def manhattan_to other
        (self.x - other.x).abs + (self.y - other.y).abs
    end

    def directions_to other
        directions = []
        directions << ?U if self.y > other.y
        directions << ?D if self.y < other.y
        directions << ?L if self.x > other.x
        directions << ?R if self.x < other.x
        directions
    end

    def one_step direction
        nx,ny = case direction
            when ?U then [self.x, self.y - 1]
            when ?D then [self.x, self.y + 1]
            when ?L then [self.x - 1, self.y]
            when ?R then [self.x + 1, self.y]
        end

        self.world[nx,ny]
    end

    def move direction
        destination = one_step(direction)
        raise "can't move there" unless destination && destination.passable?

        destination.tile, self.tile = self.tile, ?.
    end
end

class World
    DIRECTIONS = %w(U D L R)

    def initialize s
        @board = s.split.map.with_index { |l,y| l.chars.map.with_index { |c,x| Tile.new(self, c, x, y) }}
        @width = @board[0].length
    end

    def [] x,y
        y >= 0 && x >= 0 && y < @board.size && x < @board[y].size && @board[y][x]
    end

    def robots
        tiles_of_type(/[0-9]/).sort_by { |t| t.tile }
    end

    def pickles
        tiles_of_type ?P
    end

    def tiles_of_type type
        @board.flatten.select { |t| type === t.tile }
    end

    def inspect
        @board.map { |l| l.map { |t| t.tile }*'' }*?\n
    end
end

gets(nil).split("\n\n").each do |input|
    w = World.new(input)
    steps = Hash[w.robots.map { |r| [r.tile, []] }]
    while (pickles_remaining = w.pickles).size > 0
        current_targets = Hash.new(0)

        w.robots.each do |r|
            target_pickle = pickles_remaining.min_by { |p| [current_targets[p], r.manhattan_to(p)] }

            possible_moves = World::DIRECTIONS.select { |d| t = r.one_step(d); t && t.passable? }
            raise "can't move anywhere" if possible_moves.empty?

            direction = (r.directions_to(target_pickle) & possible_moves).first || possible_moves[0]

            current_targets[target_pickle] += 1
            steps[r.tile] << direction
            r.move(direction)
        end
    end

    puts steps.values.map &:join
    p steps.values.map { |v| v.size }.max
end

원래 질문에서 STDIN에 대한 테스트 케이스 코드 블록 형식으로 입력을받습니다. 해당 테스트 사례에 대해 인쇄 된 내용은 다음과 같습니다.

DDLLDLLLLULLUUD
LDLRRDDLDLLLLDR
URDDLLLLLULLUUL
15
ULDLDDLDRRRURRURDDDDDDDLLL
UUULDDRDRRRURRURDLDDDDLDLL
ULUURURRDDRRRRUUUDDDDLDLLL
26
URRRDRUDDDDLLLDLL
RUUUDLRRDDDLLLDLL
LDRDDLDDLLLLLLLUU
RUUURDRDDLLLLLUUU
17
DULLUUUUULDLDLLLLLDDRUUUUR
UDLRRRURDDLLLUUUUURDRUUUUD
26
LDDLDUUDDDUDDDDDR
ULUULDDDDDRDRDDDR
LULLDUUDDDRDRDDDR
UUUURDUURRRRDDDDD
LDLLUDDRRRRRRUDRR
17

1

파이썬, 16 + 15 + 14 + 20 + 12 = 77

나는 실제로 세일즈맨 유형의 문제를 여행 한 경험이 없지만 약간의 시간이있어서 기회를 줄 것이라고 생각했습니다. 기본적으로 각각의 봇에게 특정 피클을 걸어서 가까워 질 수있는 예비 런닝을 통해 걸을 수 있습니다. 그런 다음 각 봇이 할당 된 피클을 수집하는 가장 효율적인 방법을 무차별 대입합니다.

실제로이 방법이 얼마나 실행 가능한지 잘 모르겠지만 봇 수가 적은 대형 보드에서는 제대로 작동하지 않을 것으로 생각됩니다 (4 번째 보드는 이미 2 분 이상 소요됨).

암호:

def parse_input(string):
    pickles = []
    size = len(string) - string.count('\n')
    poses = [None] * (size - string.count('.') - string.count('P'))
    for y,line in enumerate(string.strip().split('\n')):
        for x,char in enumerate(line):
            if char == '.':
                continue
            elif char == 'P':
                pickles.append((x,y))
            else:
                poses[int(char)-1] = (x,y)
    return pickles, poses

def move((px,py),(tx,ty)):
    if (px,py) == (tx,ty):
        return (px,py)
    dx = tx-px
    dy = ty-py
    if abs(dx) <= abs(dy):
        if dy < 0:
            return (px,py-1)
        else:
            return (px,py+1)
    else:
        if dx < 0:
            return (px-1,py)
        else:
            return (px+1,py)

def distance(pos, pickle):
    return abs(pos[0]-pickle[0]) + abs(pos[1]-pickle[1])

def calc_closest(pickles,poses,index):
    distances = [[distance(pos,pickle) for pickle in pickles] for pos in poses]
    dist_diffs = []
    for i, pickle_dists in enumerate(distances):
        dist_diffs.append([])
        for j, dist in enumerate(pickle_dists):
            other = [d[j] for d in distances[:i]+distances[i+1:]]
            dist_diffs[-1].append(min(other)-dist)

    sorted = pickles[:]
    sorted.sort(key = lambda ppos: -dist_diffs[index][pickles.index(ppos)])
    return sorted

def find_best(items,level):
    if level == 0:
        best = (None, None)
        for rv, rest in find_best(items[1:],level+1):
            val = distance(items[0],rest[0]) + rv
            if best[0] == None or val < best[0]:
                best = (val, [items[0]] + rest)
        return best

    if len(items) == 1:
        return [(0,items[:])]

    size = len(items)
    bests = []
    for i in range(size):
        best = (None, None)
        for rv, rest in find_best(items[:i]+items[i+1:],level+1):
            val = distance(items[i],rest[0]) + rv
            if best[0] == None or val < best[0]:
                best = (val, [items[i]] + rest)
        if best[0] != None:
            bests.append(best)
    return bests

def find_best_order(pos,pickles):
    if pickles == []:
        return 0,[]
    best = find_best([pos]+pickles,0)
    return best

def walk_path(pos,path):
    history = ''
    while path:
        npos = move(pos, path[0])
        if npos == path[0]:
            path.remove(path[0])

        if npos[0] < pos[0]:
            history += 'L'
        elif npos[0] > pos[0]:
            history += 'R'
        elif npos[1] < pos[1]:
            history += 'U'
        elif npos[1] > pos[1]:
            history += 'D'
        pos = npos
    return history

def find_paths(input_str):
    pickles, poses = parse_input(input_str)                 ## Parse input string and stuff
    orig_pickles = pickles[:]
    orig_poses = poses[:]
    numbots = len(poses)

    to_collect = [[] for i in range(numbots)]               ## Will make a list of the pickles each bot should go after
    waiting = [True] * numbots
    targets = [None] * numbots
    while pickles:
        while True in waiting:                              ## If any bots are waiting for a new target
            index = waiting.index(True)
            closest = calc_closest(pickles,poses,index)     ## Prioritizes next pickle choice based upon how close they are RELATIVE to other bots
            tar = closest[0]

            n = 0
            while tar in targets[:index]+targets[index+1:]:                 ## Don't target the same pickle!
                other_i = (targets[:index]+targets[index+1:]).index(tar)
                dist_s = distance(poses[index],tar)
                dist_o = distance(poses[other_i],tar)
                if dist_s < dist_o:
                    waiting[other_i] = True
                    break

                n += 1
                if len(closest) <= n:
                    waiting[index] = False
                    break
                tar = closest[n]

            targets[index] = tar
            waiting[index] = False      

        for i in range(numbots):                            ## Move everything toward targets  (this means that later target calculations will not be based on the original position)
            npos = move(poses[i], targets[i])
            if npos != poses[i]:
                poses[i] = npos
            if npos in pickles:
                to_collect[i].append(npos)
                pickles.remove(npos)
                for t, target in enumerate(targets):
                    if target == npos:
                        waiting[t] = True               

    paths = []
    sizes = []

    for i,pickle_group in enumerate(to_collect):                    ## Lastly brute force the most efficient way for each bot to collect its allotted pickles
        size,path = find_best_order(orig_poses[i],pickle_group)
        sizes.append(size)
        paths.append(path)
    return max(sizes), [walk_path(orig_poses[i],paths[i]) for i in range(numbots)]

def collect_pickles(boards):
    ## Collect Pickles!
    total = 0
    for i,board in enumerate(boards):
        result = find_paths(board)
        total += result[0]
        print "Board "+str(i)+": ("+ str(result[0]) +")\n"
        for i,h in enumerate(result[1]):
            print '\tBot'+str(i+1)+': '+h
        print

    print "Total Score: " + str(total)

boards = """
P.......1.
..........
P.....P...
..P.......
....P2....
...P.P....
.PP..P....
....P....P
PPPP....3.
.P..P.P..P

....P.....
P....1....
.P.....PP.
.PP....PP.
.2.P.P....
..P....P..
.P........
.....P.P..
P.....P...
.3.P.P....

..P..P..P.
..1....P.P
..........
.......2P.
...P....P3
.P...PP..P
.......P.P
..P..P..PP
..P.4P..P.
.......P..

..P...P...
.....P....
PPPP...P..
..P.......
...P......
.......P.1
.P..P....P
P2PP......
.P..P.....
..........

......PP.P
.P1..P.P..
......PP..
P..P....2.
.P.P3.....
....4..P..
.......PP.
..P5......
P.....P...
....PPP..P
""".split('\n\n')

collect_pickles(boards)

산출:

Board 0: (16)

    Bot1: DLDLLLLDLLULUU
    Bot2: LDLDLLDDLDRURRDR
    Bot3: URDDLLLULULURU

Board 1: (15)

    Bot1: ULRDRDRRDLDDLUL
    Bot2: DDURURULLUUL
    Bot3: ULRRDRRRURULRR

Board 2: (14)

    Bot1: URRRDDDDDRLLUL
    Bot2: UUURDRDDLD
    Bot3: DDDLDDLUUU
    Bot4: RULLLDUUUL

Board 3: (20)

    Bot1: DLULUUUUULDLLLULDDD
    Bot2: LURDDURRDRUUUULUULLL

Board 4: (12)

    Bot1: LDDLDR
    Bot2: ULUULRRR
    Bot3: LUURURDR
    Bot4: RRRDRDDDR
    Bot5: LLDLRRRDRRRU

Total Score: 77
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.