Robotlar! Bu turşuları topla!


10

Kendimi biraz turşu haline getirmiş gibiyim. Kelimenin tam anlamıyla. Zemine bir avuç turşu düşürdüm ve şimdi hepsi dağılmış durumda! Hepsini toplamama yardım etmene ihtiyacım var. Ah, emrimde bir sürü robotum olduğunu söylemiş miydim? (Hepsi de her yere dağılmışlar; Bir şeyleri organize etmede gerçekten kötüyüm.)

Girdiyi şu şekilde almalısınız:

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

yani, ya birden fazla satır ., P(turşu) veya bir rakam (robotun İD). (Her satırın eşit uzunlukta olduğunu, dolgulu olduğunu varsayabilirsiniz ..) Bu satırları bir dizi olarak girebilir veya STDIN'den ayrılabilir veya virgülle ayrılmış tek bir satırda okuyabilir veya bir dosyayı okuyabilir veya ne istersen yapabilirsiniz girişi almak istiyorum.

Çıktınız , en yüksek robot kimliğinin nbulunduğu satırlar şeklinde olmalıdır n. (Robot kimlikleri her zaman 1'den başlayarak sıralı olacaktır.) Her satır robotun L(sol), R(sağ), U(yukarı) ve D(aşağı) harflerinden oluşan yolunu içerecektir . Örneğin, bu bulmacanın örnek çıktısı:

LLU
RDR
LRRR
D

Ayrıca olabilir

LLU RDR LRRR D

Veya

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

Ya da çözümün ne olması gerektiğini söyleyebildiğiniz sürece istediğiniz herhangi bir format.

Amacınız, en az adıma sahip olan optimum çıktıyı bulmaktır. Adım sayısı, tüm robotların en büyük adım sayısı olarak sayılır. Örneğin, yukarıdaki örnekte 4 adım vardı. Birden fazla çözüm olabileceğini unutmayın, ancak yalnızca bir çözüm üretmeniz gerekir.

puanlama:

  • Programınız 5 (rastgele oluşturulmuş) test vakasının her biri ile çalıştırılacaktır.
  • Her koşudan adımları eklemelisiniz ve bu sizin puanınız olacaktır.
  • En düşük toplam, kümülatif puan kazanacaktır.
  • Bu belirli girişler için sabit kodlama yapamayabilirsiniz. Kodunuz diğer girdiler için de çalışmalıdır.
  • Robotlar birbirinden geçebilir.
  • Programınız deterministik olmalıdır, yani her çalışma için aynı çıktı. Tohumlanmış ve sürekli olarak aynı sayıları çapraz platform ürettiği sürece rastgele bir sayı üreteci kullanabilirsiniz.
  • Her giriş için kodunuzun 3 dakika içinde çalışması gerekir. (Tercihen çok daha az.)
  • Beraberlik durumunda, çoğu oylar kazanır.

İşte test senaryoları. Yazdığım küçük bir Ruby senaryosuyla rastgele oluşturuldular.

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

İyi şanslar ve turşuların orada çok uzun süre oturmasına izin vermeyin, yoksa bozulurlar!


Oh, neden turşu istiyorsun?

Neden olmasın?


3
Aslında "ideal çıktıyı" bulduğunuzu göstermenin makul bir yolu yoktur, çünkü bu aslında seyahat eden bir satıcı (erkek) problemidir ve NP tamdır.
Wally

@ Wally Hmm, öyle mi? Belki biri sağlanan test senaryosu için asgari adımları bulmalı ve sonra tüm cevaplar buna dayanabilir.
Kapı tokmağı

2
Test durumu muhtemelen bir kişi bunu yapmak istiyorsa, kuvveti asgariye indirecek kadar küçüktür. Ve / veya cevap veren herkes test senaryosu için ne aldıklarını söyleyebilir ve en azından bu minimum değere uyması için başka cevaplar isteyebilirsin.
Wally

3
Robotlar birbirinden geçebilir mi? Değilse, yolları yorumlamadaki zamanlama kısıtlamaları nelerdir?
Peter Taylor

1
@Gareth Buradaki problem, test senaryoları ortaya çıkana kadar puanların bilinmeyeceği ve daha sonra yapılan testlerin zaten test senaryolarını göreceği.
Kapı tokmağı

Yanıtlar:


6

Yakut, 15 + 26 + 17 + 26 + 17 = 101

Robot Turşu bulur!

Tamam, aşağıdaki süper naif algoritmayı kullanarak insanları başlatmak için bir temel:

  • Her bir kene, her bir robot aşağıdaki adımları uygulayarak sayısal sırada hareket edecektir:
    • Başka hiçbir robotun hedeflemediği en yakın (Manhattan mesafesi) turşusunu belirleyin.
    • Hangi bitişik karelerin taşınabileceğini belirleyin.
    • Seçili turşuya yaklaştıran yönleri tercih ederek bu karelerden birini seçin.

Test Durumu # 1 için nasıl görünüyor:

TC1 için Hareketli Örnek

Açıkçası bu çok iyi değil ama bir başlangıç!

Kod:

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 üzerine girdiyi tam olarak orijinal sorudaki test senaryosu kod bloğu formatında alır. İşte bu test senaryoları için yazdıkları:

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

Seyahat satıcısı tipi problemlerle ilgili daha önce hiç deneyimim yok ama ellerimde biraz zaman geçirdim, bu yüzden denemek istedim. Temelde, her botu belirli turşuları yürüyüş yaparak kendilerine en yakın olanlara ve diğerlerinden en uzak olanlara gidecekleri bir ön koşuya ayırmaya çalışır. Daha sonra her bot için ayrılan turşularını toplamanın en etkili yolunu kaba kuvvetler.

Bu yöntemin ne kadar geçerli olduğunu gerçekten bilmiyorum, ancak daha az botu olan daha büyük tahtalar için işe yaramayacağından şüpheleniyorum (dördüncü tahta zaten bazen iki dakikadan fazla sürüyor).

Kod:

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)

Çıktı:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.