นักวิทยาศาสตร์ผนึกบนภูเขาน้ำแข็ง


17

บทนำ

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

ป้อนคำอธิบาย

โปรแกรมของซีลจะรับอินพุตจาก STDIN อาร์กิวเมนต์บรรทัดคำสั่งหรือฟังก์ชั่นอินพุตผู้ใช้ (เช่นraw_input()) ไม่สามารถกำหนดค่าเริ่มต้นล่วงหน้าในตัวแปรได้ (เช่น "โปรแกรมนี้คาดว่าอินพุตจะอยู่ในตัวแปรx")

บรรทัดแรกของอินพุตประกอบด้วยสองจำนวนเต็มคั่นด้วยเครื่องหมายจุลภาคในแบบฟอร์ม

A,B

ต่อไปนี้เป็นBบรรทัดที่ประกอบด้วยAอักขระแต่ละตัว แต่ละบรรทัดสามารถมีอักขระได้ไม่เกินรายการต่อไปนี้:

  • .: ความเย็นความหนาวเย็นมหาสมุทร แผนที่จะมีสิ่งนี้เป็นเส้นขอบเสมอ
  • #: ส่วนหนึ่งของภูเขาน้ำแข็ง
  • a... z: ตราประทับที่ไม่ใช่ตราประทับของพ่อบนภูเขาน้ำแข็ง
  • D: พ่อผนึกบนภูเขาน้ำแข็ง
  • *: เครื่องส่งสัญญาณวิทยุ

(โปรดทราบว่าตราประทับของพ่อนั้นจะถูกระบุด้วยตัวพิมพ์ใหญ่Dเสมอตัวพิมพ์เล็กdเป็นเพียงตราประทับปกติ)

คำอธิบายผลลัพธ์

ตามกฎต่อไปนี้เกี่ยวกับวิธีที่ซีลสามารถเคลื่อนย้ายได้ให้ส่งรายการคำแนะนำสำหรับแมวน้ำว่าพวกเขาควรจะเคลื่อนย้ายอย่างไรเพื่อให้ผนึกผนึกพ่อกับเครื่องส่งสัญญาณวิทยุ

  1. กฎ:แมวน้ำทั้งหมดสามารถเลื่อนขึ้น ( U), ลง ( D), ซ้าย ( L) และขวา ( R) พวกเขาไม่สามารถเลื่อนในแนวทแยงมุม
  2. กฎ:เมื่อเคลื่อนที่แมวน้ำจะเคลื่อนที่ต่อไปในทิศทางเดียวกันจนกระทั่งมันชนกับแมวน้ำอื่นหรือตกลงไปในทะเล
    1. หากตราประทับชนกับตราประทับอื่นมันจะหยุดเคลื่อนที่ ผนึกที่มันชนเข้ากับจะไม่เคลื่อนที่
    2. หากแมวน้ำตกลงไปในทะเลมันจะจมลงและหายไปจากแผนที่ นั่นคือมันไม่ได้ทำหน้าที่เป็น collider สำหรับแมวน้ำอื่นและไม่สามารถเคลื่อนย้ายได้อีก
  3. กฎ:แมวน้ำสองตัวไม่สามารถเคลื่อนที่ได้ในเวลาเดียวกันและไม่สามารถเคลื่อนย้ายแมวน้ำได้ในขณะที่อีกอันหนึ่งยังเคลื่อนไหวอยู่ ตราประทับถัดไปสามารถเคลื่อนย้ายได้เฉพาะเมื่อตราประทับก่อนหน้าหยุดการเคลื่อนที่
  4. กฎ:ไม่มีข้อ จำกัด เกี่ยวกับการย้ายตราประทับหลายครั้งหรือจำนวนของแมวน้ำที่จมน้ำ
  5. กฎข้อที่:วิธีการแก้ปัญหาที่ถูกต้องจะมีตราประทับพ่อสิ้นสุดที่เครื่องส่งสัญญาณวิทยุ ตราประทับของพ่อไม่สามารถส่งผ่านตัวส่งสัญญาณได้ในขณะที่เลื่อน

เอาต์พุตจะประกอบด้วยหลายบรรทัดแต่ละบรรทัดในแบบฟอร์ม

A,B

ในกรณีที่Aเป็นตราประทับที่จะย้าย ( Dสำหรับประทับตราพ่อa... zสำหรับคนอื่น ๆ ) และBเป็นทิศทางที่จะย้ายประทับตรา (อย่างใดอย่างหนึ่งU, D, LหรือR) โปรดทราบว่าคุณไม่จำเป็นต้องค้นหาเส้นทางที่สั้นที่สุด เส้นทางที่ได้รับตราพ่อไปยังเป้าหมายคือผลลัพธ์ที่ยอมรับได้

ตัวอย่างอินพุตและเอาต์พุต

การป้อนข้อมูล:

25,5
.........................
.#######################.
.####D#############*k###.
.#######################.
.........................

เอาท์พุท:

D,R

การป้อนข้อมูล:

9,7
.........
.a#####b.
.#####d#.
.##l*###.
.###m#p#.
.#D#.#c#.
.........

เอาท์พุท (เป็นไปได้หนึ่งในหลาย ๆ ):

m,R
b,L
D,U
D,R
D,D
D,L

การป้อนข้อมูล:

26,5
..........................
.###..................###.
.l*##########v#########D#.
.###..................###.
..........................

เอาท์พุท (เป็นไปได้หนึ่งในหลาย ๆ ):

v,D
D,L

หากคุณมีคำถามอื่น ๆ โปรดถามในความคิดเห็น


อินพุตทั้งหมดจะมีโซลูชันที่ถูกต้องหรือไม่ ถ้าไม่คาดหวังสิ่งที่ผลลัพธ์ / พฤติกรรม?
Geobits

@Geobits อินพุตทั้งหมดจะมีวิธีการแก้ไขที่ถูกต้อง อินพุตที่ไม่มีโซลูชันจะถือว่าไม่ถูกต้องและโปรแกรมของคุณสามารถทำอะไรกับมันได้
absinthe

อนุญาตให้จบโปรแกรมด้วยการโยนข้อยกเว้นได้หรือไม่?
DLosc

2
จะเกิดอะไรขึ้นถ้าตราประทับที่ไม่ใช่พ่อกระทบเครื่องส่งวิทยุ? มันจะหยุดหรือผ่านมันไปหรือไม่
Reto Koradi

1
นั่นทำให้โซลูชันของฉันใช้ไม่ได้ :(
DLosc

คำตอบ:


6

Python 3, 520 ไบต์

R=range
g=[list(input())for i in R(int(input().split(',')[1]))]
f=set(sum(g,[]))-set(".#*")
L=8
def P(p,g):
 if len(p)>L:return
 for s in f:
  c=sum(y.index(s)for y in g if s in y)
  if c<1:continue
  r,=[n for n in R(len(g))if s in g[n]]
  for d in R(4):
   m=p+s+",%s\n"%"LURD"[d];G=[y[:]for y in g];o="#";i,j=I,J=r,c
   while"#"==o:G[i][j]="#";G[I][J]=s;i,j=I,J;I,J=i+d%2*(d-2),j+(~d%-2&d-1);o=G[I][J]
   if"."==o:G[i][j]="#"
   if"D"==s:
    if"."==o:continue
    if"*"==o:print(m);1/0
   P(m,G)
while 1:P("",g);L+=4

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

ฉันสามารถทำให้โค้ดทำงานได้เร็วขึ้นอย่างมีนัยสำคัญโดยเพิ่มif G!=g:ที่จุดเริ่มต้นของบรรทัดที่สองสำหรับ 8 ไบต์พิเศษ - นี่เป็นการปฏิเสธการย้ายที่ไม่เปลี่ยนแปลงอะไรเช่นk,Lในกรณีทดสอบครั้งแรก

รันไทม์แตกต่างกันอย่างเห็นได้ชัดจากการวิ่งครั้งต่อไปถึงแม้จะมีการป้อนข้อมูลเดียวกัน - เห็นได้ชัดว่าเป็นผลมาจากความจริงที่ว่าฉันเลือกตราประทับต่อไปที่จะย้ายโดยวนซ้ำsetซึ่งเป็นประเภทที่ไม่ได้สั่ง ฉันหมดเวลาในการทดสอบครั้งที่สองที่ 5 นาที 30 วินาทีแม้ว่ามันจะดูไม่นานนักในครั้งแรกที่ฉันวิ่ง ด้วยการเพิ่มประสิทธิภาพดังกล่าวข้างต้นจะมีระยะเวลา 40 วินาที


1
สิ่งที่น่าสนใจใน Python 2 ก็ควรให้ลำดับเดียวกันทุกครั้ง ฉันคิดว่าพวกเขาเปลี่ยน Python 3 เพื่อให้แฮชแบบสุ่มในแต่ละครั้งสำหรับวัตถุเดียวกันเพื่อหลีกเลี่ยงการหาประโยชน์บางอย่าง: "การสุ่มการแฮชถูกเปิดใช้งานโดยค่าเริ่มต้นตั้งค่าตัวแปรสภาพแวดล้อม PYTHONHASHSEED เป็น 0 เพื่อปิดการใช้งานการสุ่มแฮช วิธี."
Claudiu

4

JavaScript (ES6) 322 334 323

Edit2เพิ่มภาพเคลื่อนไหวในตัวอย่าง

แก้ไขการแก้ไขข้อผิดพลาดโปรดจำตำแหน่งเริ่มต้นของ '*' ดังนั้นฉันจึงพบว่าแม้แมวน้ำจะปิดทับและลบทิ้ง

นำมาใช้เป็นฟังก์ชั่นที่มีสตริงอินพุตเป็นพารามิเตอร์ (อาจไม่ถูกต้อง แต่รอการชี้แจง) ส่งออกผ่านป๊อปอัพ
ปัญหาเกี่ยวกับอินพุตสตริงหลายบรรทัดใน JavaScript คือpromptไม่สามารถจัดการได้ดี

สำหรับอัลกอริทึม: BFS ควรหาทางออกที่ดีที่สุด ฉันเก็บสถานะของคิวของเกมไว้ในตัวแปรlสถานะจะเรียงตามกริดตัวละครgและลำดับของการเคลื่อนไหวจนถึงตอนsนี้ นอกจากนี้ยังมีชุดของกริดที่ได้รับมาจนถึงในตัวแปรkเพื่อหลีกเลี่ยงการสำรวจกริดเดียวกันซ้ำแล้วซ้ำอีก

วงหลักคือ

  • ยกเลิกสถานะของเกม
  • ลองใช้การเคลื่อนไหวที่เป็นไปได้ทั้งหมดเข้าคิวสถานะหลังจากการย้ายแต่ละครั้งที่ถูกต้อง (IIF ตารางผลลัพธ์ไม่ได้มีอยู่แล้ว)
  • หากพบวิธีแก้ปัญหาให้ออกจากลูป
F=s=>{
  o=~s.match(/\d+/),g=[...z=s.replace(/.*/,'')];
  for(l=[[g,'']],k=[];[g,s]=l.shift(),!g.some((c,p)=>
      c>'A'&&[-1,1,o,-o].some((d,j)=>{
        t=s+' '+[c,'LRUD'[j]];
        for(q=p;(u=g[q+d])<'.';)q+=d;
        return(c<'a'&z[q]=='*')||
        c>'D'|u>'.'&&!(
          f=[...g],u=='.'?0:f[q]=c,f[p]='#',
          k[h=f.map(v=>v>'D'?0:v)]||(k[h]=l.push([f,t]))
        )
      })
    ););
  alert(t)
}

เรียกใช้ Snippet เพื่อทดสอบใน FireFox


1

C ++, 628 ไบต์

นี่มันไม่ได้สั้นมาก:

#include <set>
#include <iostream>
using namespace std;struct R{string b,m;bool operator<(R r)const{return b<r.b;}};int w,h,t,j,k,z=1;char c,f;set<R> p,q;int m(R r,int x,int d,char a){for(j=x,c=r.b[x];(f=r.b[j+=d])==35;);if(c-68||f-46){r.b[x]=35;if(f-46)r.b[j-d]=c;r.m+=c;r.m+=44;r.m+=a;r.m+=10;if(c==68&j-d==t){cout<<r.m;z=0;}if(p.count(r)+q.count(r)==0){q.insert(r);}}}int main(){cin>>w>>c>>h>>c;R r;string l;for(;k++<h;){getline(cin,l);r.b+=l;}t=r.b.find(42);r.b[t]=35;q.insert(r);for(;z;){r=*q.begin();q.erase(q.begin());p.insert(r);for(k=0;z&&k<w*h;++k){if(r.b[k]>64){m(r,k,-1,76);m(r,k,1,82);m(r,k,-w,85);m(r,k,w,68);}}}}

ฉันเลือก C ++ เพราะฉันต้องการใช้โครงสร้างข้อมูล ( set, string) แต่มันค่อนข้างละเอียด วิธีการแก้ปัญหามีประสิทธิภาพดีพอสมควรแก้การทดสอบ 2 ในเวลาน้อยกว่า 2 วินาทีบน MacBook Pro แม้ว่าจะไม่ได้รับการปรับให้เหมาะสมสำหรับการใช้งานจริง

โค้ดก่อนที่จะเริ่มกำจัด whitespace และการลดความยาวอื่น ๆ :

#include <set>
#include <iostream>

using namespace std;

struct R {
    string b, m;
    bool operator<(R r) const {return b < r.b; }
};

int w, h, t;
set<R> p, q;
bool z = true;

void m(R r, int k, int d, char a) {
    int j = k;
    char s = r.b[k], f;
    for (; (f = r.b[j += d]) == 35;);
    if (s - 68 || f - 46) {
        r.b[k] = 35;
        if (f - 46) {
            r.b[j - d] = s;
        }
        r.m += s;
        r.m += 44;
        r.m += a;
        r.m += 10;
        if (s == 68 && j - d == t) {
            cout << r.m;
            z = false;
        }
        if (p.count(r) + q.count(r) == 0) {
            q.insert(r);
        }
    }
}

int main() {
    char c;
    cin >> w >> c >> h >> c;
    string b, l;
    int k;
    for (k = 0; k < h; ++k) {
        getline(cin, l);
        b += l;
    }

    t = b.find(42);
    b[t] = 35;

    R r;
    r.b = b;
    q.insert(r);

    for ( ; z; ) {
        r = *q.begin();
        q.erase(q.begin());
        p.insert(r);

        for (k = 0; z && k < w * h; ++k) {
            c = r.b[k];
            if (c > 64) {
                m(r, k, -1, 76);
                m(r, k, 1, 82);
                m(r, k, -w, 85);
                m(r, k, w, 68);
            }
        }
    }

    return 0;
}

แนวคิดหลักที่อยู่เบื้องหลังอัลกอริทึมคือรักษาสองชุดไว้:

  • q เป็นชุดของการกำหนดค่าที่อยู่ระหว่างดำเนินการ
  • p เป็นชุดของการกำหนดค่าที่ได้รับการประมวลผล

qขั้นตอนวิธีการเริ่มต้นด้วยการกำหนดค่าเริ่มต้นใน ในทุกขั้นตอนจะมีการเพิ่มการกำหนดค่าqลงในpqทุกการเคลื่อนไหวที่เป็นไปได้ประทับตราถูกสร้างขึ้นและส่งผลให้การกำหนดค่าใหม่ใส่เข้าไปใน

ทดสอบการทำงาน:

bash-3.2$ ./a.out <test1
D,R
bash-3.2$ time ./a.out <test2
p,U
c,U
p,R
c,L
m,L
b,L
a,L
D,U
b,L
D,R
D,D
D,L

real    0m2.267s
user    0m2.262s
sys 0m0.003s
bash-3.2$ ./a.out <test3
v,U
D,L
bash-3.2$

ใช้คิวแทนการตั้งค่าสำหรับ 'q' คุณสามารถค้นหาวิธีแก้ปัญหาที่สั้นกว่าในเวลาที่น้อยลง (โซลูชันของฉันสำหรับการทดสอบ 2 คือ 6 ขั้นตอน)
edc65

@ edc65 ใช่ฉันแปลกใจในตอนแรกว่ามีจำนวนการเคลื่อนไหวที่นั่น ฉันเดินผ่านมันเพื่อยืนยันว่ามันเป็นทางออกที่ถูกต้อง การใช้ FIFO qจะดีกว่าแน่นอน เหตุผลที่ฉันใช้ชุดคือฉันต้องการหลีกเลี่ยงการป้อนรายการเดียวกันหลายครั้ง แต่ฉันเริ่มมีความคิดที่สองเมื่อฉันเห็นผลลัพธ์
Reto Koradi

ประสิทธิภาพควรใช้คิวและชุดเพื่อหลีกเลี่ยงการทำซ้ำ แต่ใส่กริดในเวอร์ชั่นที่ถูกปรับตั้งไว้เนื่องจากตราประทับทารกแต่ละอันสามารถใช้แทนกันได้
edc65
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.