หุ่นยนต์! รวบรวมผักดองเหล่านี้!


10

ฉันดูเหมือนจะทำให้ตัวเองกลายเป็นของดอง อย่างแท้จริง ฉันทิ้งผักดองไว้บนพื้นและตอนนี้พวกมันกระจัดกระจายไปหมด! ฉันต้องการให้คุณช่วยรวบรวมพวกเขาทั้งหมด โอ้ฉันไม่ได้พูดถึงว่าฉันมีหุ่นยนต์จำนวนมากอยู่ที่คำสั่งของฉันหรือไม่? (พวกมันกระจัดกระจายไปหมดทั่วทั้งสถานที่; ฉันแย่มากในการจัดระเบียบสิ่งต่าง ๆ )

คุณต้องป้อนข้อมูลในรูปแบบของ:

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

เช่นหลายบรรทัดอย่างใดอย่างหนึ่ง., P(ดอง) หรือหลัก (ID ของหุ่นยนต์) (คุณอาจสมมติว่าแต่ละบรรทัดมีความยาวเท่ากันเต็มไปด้วย.) คุณสามารถป้อนบรรทัดเหล่านี้เป็นอาร์เรย์หรือ slurp จาก STDIN หรืออ่านในบรรทัดเดียวที่คั่นด้วยเครื่องหมายจุลภาคหรืออ่านไฟล์หรือทำสิ่งที่คุณต้องการ ชอบที่จะใส่

ผลลัพธ์ของคุณจะต้องอยู่ในรูปแบบของnเส้นตรงซึ่งnเป็นรหัสหุ่นยนต์สูงสุด (รหัสประจำตัวหุ่นยนต์จะเรียงตามลำดับโดยเริ่มต้นที่ 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
ไม่มีวิธีที่สมเหตุสมผลที่จะแสดงให้เห็นว่าคุณพบ "การส่งออกที่ดีที่สุด" จริง ๆ แล้วนี่เป็นปัญหาพนักงานขายที่เดินทาง (ผู้ชาย) และเป็นปัญหาที่สมบูรณ์
Wally

@ เก่งอืมมันคืออะไร? บางทีใครบางคนควรหาขั้นต่ำสำหรับกรณีทดสอบที่มีให้และจากนั้นคำตอบทั้งหมดจะขึ้นอยู่กับว่า
Doorknob

2
กรณีทดสอบอาจมีขนาดเล็กพอที่จะเดรัจฉานบังคับให้น้อยที่สุด - ถ้ามีคนต้องการทำเช่นนั้น และ / หรือทุกคนที่ตอบว่าสามารถบอกสิ่งที่พวกเขาได้รับสำหรับกรณีทดสอบและคุณอาจต้องการคำตอบอื่น ๆ อย่างน้อยตรงกับขั้นต่ำนั้น
Wally

3
หุ่นยนต์สามารถผ่านกันและกันได้หรือไม่? หากไม่มีข้อ จำกัด เวลาเกี่ยวกับการตีความเส้นทางคืออะไร
Peter Taylor

1
@Gareth ปัญหาที่เกิดขึ้นคือคะแนนนั้นจะไม่เป็นที่รู้จักจนกว่าจะมีการเปิดเผยผลการทดสอบแล้วหลังจากนั้นจะเห็นผลการทดสอบแล้ว
Doorknob

คำตอบ:


6

Ruby, 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

Python 16 + 15 + 14 + 20 + 12 = 77

ฉันไม่เคยมีประสบการณ์มาก่อนเกี่ยวกับปัญหาประเภทพนักงานขายที่เดินทาง แต่ฉันมีเวลาอยู่ในมือของฉันดังนั้นฉันคิดว่าฉันจะให้มันยิง โดยทั่วไปแล้วมันพยายามที่จะจัดสรรบอทผักดองบางชนิดโดยการเดินมันแม้ว่าจะเป็นการวิ่งขั้นต้นที่พวกเขาจะไปหาคนที่อยู่ใกล้พวกเขามากที่สุดและไกลที่สุดจากคนอื่น ๆ จากนั้นมันจะบังคับวิธีที่มีประสิทธิภาพที่สุดสำหรับแต่ละบอทเพื่อรวบรวมผักดองที่ได้รับมา

ฉันไม่รู้ว่าวิธีนี้ใช้ได้จริง แต่ฉันคิดว่ามันจะไม่ทำงานได้ดีสำหรับบอร์ดขนาดใหญ่ที่มีบอทน้อยลง (บอร์ดที่สี่บางครั้งใช้เวลากว่าสองนาที)

รหัส:

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.