Con dấu nhà khoa học bị mắc kẹt trên một tảng băng trôi


17

Giới thiệu

Một gia đình hải cẩu bị mắc kẹt trên một tảng băng ở Vòng Bắc Cực. Có một máy phát radio được đặt trên tảng băng trôi mà hải cẩu có thể sử dụng để gọi trợ giúp. Tuy nhiên, chỉ có con dấu cha mới biết cách vận hành máy phát radio. Và tệ hơn nữa, băng rất trơn vào thời điểm này trong năm, do đó, hải cẩu sẽ trượt không kiểm soát được cho đến khi chúng đâm vào một con dấu khác hoặc trượt ra khỏi rìa tảng băng, khiến cho con hải cẩu rất khó tiếp cận máy phát radio. May mắn thay, một trong những con dấu là một nhà khoa học máy tính, vì vậy cô quyết định viết một chương trình để tìm ra cách điều khiển con dấu của cha đến máy phát radio. Vì không có nhiều không gian trên băng để viết chương trình, cô quyết định làm cho chương trình sử dụng càng ít byte càng tốt.

Mô tả đầu vào

Chương trình của con dấu sẽ lấy đầu vào từ STDIN, đối số dòng lệnh hoặc chức năng nhập của người dùng (chẳng hạn như raw_input()). Nó không thể được khởi tạo trước trong một biến (ví dụ: "Chương trình này mong đợi đầu vào trong một biếnx ").

Dòng đầu tiên của đầu vào bao gồm hai số nguyên được phân tách bằng dấu phẩy ở dạng

A,B

Theo sau đó là Bcác dòng bao gồm các Aký tự mỗi. Mỗi dòng chỉ có thể chứa các ký tự sau:

  • .: Trời lạnh, lạnh, đại dương. Bản đồ sẽ luôn có điều này như là một biên giới.
  • #: Một phần của tảng băng trôi.
  • a... z: Một con dấu không phải là con dấu của cha trên tảng băng trôi.
  • D: Con dấu cha trên tảng băng trôi.
  • *: Máy phát vô tuyến.

(Lưu ý rằng con dấu cha luôn được ký hiệu bằng chữ hoa D. Chữ thường dchỉ đơn giản là một con dấu thông thường.)

Mô tả đầu ra

Theo các quy tắc sau đây về cách con dấu có thể di chuyển, hãy đưa ra một danh sách các hướng dẫn cho con dấu về cách chúng nên di chuyển để đưa con dấu bố đến máy phát radio.

  1. Quy tắc: Tất cả các con dấu có thể di chuyển lên ( U), xuống ( D), trái ( L) và phải ( R). Họ không thể trượt theo đường chéo.
  2. Qui định: Khi di chuyển, một con hải cẩu sẽ tiếp tục di chuyển theo cùng một hướng cho đến khi nó va chạm với một con dấu khác hoặc rơi xuống biển.
    1. Nếu một con dấu va chạm với một con dấu khác, nó sẽ ngừng di chuyển. Con dấu nó va chạm với sẽ không di chuyển.
    2. Nếu một con hải cẩu rơi xuống biển, nó sẽ chết đuối và biến mất khỏi bản đồ. Đó là, nó không hoạt động như một máy va chạm cho các con dấu khác và nó không thể được di chuyển lại.
  3. Quy tắc: Hai con dấu không thể di chuyển cùng một lúc, con dấu cũng không thể di chuyển trong khi một con dấu khác vẫn đang di chuyển. Con dấu tiếp theo chỉ có thể được di chuyển khi con dấu trước đó đã ngừng di chuyển.
  4. Quy tắc: Không có hạn chế về việc di chuyển một con dấu nhiều lần, hoặc số lượng con dấu bị chết đuối.
  5. Quy tắc: Một giải pháp chính xác sẽ có kết thúc con dấu cha ở máy phát radio. Con dấu cha không thể đơn giản vượt qua máy phát trong khi trượt.

Đầu ra sẽ bao gồm một số dòng, mỗi dòng trong mẫu

A,B

Trong trường hợp Alà con dấu để di chuyển ( Dđối với con dấu cha, a... zcho người khác), và Blà định hướng để di chuyển con dấu (hoặc U, D, L, hoặc R). Lưu ý rằng bạn không cần phải tìm con đường ngắn nhất. Bất kỳ tuyến đường nào có được con dấu cha đến mục tiêu là một đầu ra chấp nhận được.

Ví dụ đầu vào và đầu ra

Đầu vào:

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

Đầu ra:

D,R

Đầu vào:

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

Đầu ra (một đầu ra có thể trong số nhiều):

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

Đầu vào:

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

Đầu ra (một đầu ra có thể trong số nhiều):

v,D
D,L

Nếu bạn có bất kỳ câu hỏi nào khác, xin vui lòng hỏi trong các ý kiến.


Tất cả các đầu vào sẽ có một giải pháp hợp lệ? Nếu không, đầu ra / hành vi dự kiến ​​là gì?
Geobits

@Geobits Tất cả các đầu vào sẽ có một giải pháp hợp lệ. Đầu vào không có giải pháp được coi là không hợp lệ và chương trình của bạn có thể làm bất cứ điều gì với chúng.
absinthe

Có được phép kết thúc chương trình bằng cách ném một ngoại lệ không?
DLosc

2
Điều gì xảy ra nếu một con dấu không phải bố chạm vào máy phát radio? Nó sẽ dừng lại, hoặc di chuyển qua nó?
Reto Koradi

1
Điều đó làm mất hiệu lực giải pháp của tôi. :(
DLosc

Câu trả lời:


6

Python 3, 520 byte

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

Tôi có thể giải thích chi tiết hơn sau này nếu mọi người muốn, nhưng về cơ bản, điều này thực hiện tìm kiếm theo chiều sâu với độ sâu lặp đi lặp lại trên không gian trạng thái của các chuyển động có thể. Nếu một động thái làm cho con dấu cha rơi ra, nó ngay lập tức bị từ chối. Nếu người cha kết thúc bên cạnh máy phát, chuỗi di chuyển sẽ được in và chương trình chia cho 0 để buộc thoát ra.

Tôi có thể làm cho mã chạy nhanh hơn đáng kể bằng cách thêm if G!=g:vào đầu dòng thứ hai, thêm 8 byte - điều này từ chối các động thái không thay đổi bất cứ điều gì, chẳng hạn nhưk,L trong trường hợp thử nghiệm đầu tiên.

Thời gian chạy thay đổi đáng chú ý từ lần chạy này sang lần chạy tiếp theo, ngay cả với cùng một đầu vào - rõ ràng là kết quả của việc tôi chọn con dấu tiếp theo để di chuyển bằng cách lặp qua một set, đó là một loại không có thứ tự. Tôi đã hẹn giờ cho trường hợp thử nghiệm thứ hai vào lúc 5 phút 30 giây, mặc dù lần đầu tiên tôi chạy nó không lâu. Với tối ưu hóa được đề cập ở trên, nó giống như 40 giây.


1
Điều thú vị là trong Python 2, nó sẽ cho cùng một thứ tự mỗi lần. Tôi nghĩ rằng họ đã thay đổi Python 3 để đưa ra các giá trị băm ngẫu nhiên trên mỗi lần chạy cho cùng một đối tượng để tránh việc khai thác nhất định: "Tính ngẫu nhiên băm được bật theo mặc định. Đặt biến môi trường PYTHONHASHSEED thành 0 để tắt ngẫu nhiên băm. Xem thêm đối tượng .__ hash __ () phương pháp."
Claudiu

4

JavaScript (ES6) 322 334 323

Edit2 Đã thêm hình ảnh động trong đoạn trích

Chỉnh sửa sửa lỗi, ghi nhớ vị trí ban đầu của '*', vì vậy tôi tìm thấy nó ngay cả khi một con dấu trượt qua nó và xóa nó.

Được triển khai như một hàm với chuỗi đầu vào là tham số (có thể không hợp lệ nhưng đang chờ làm rõ). Đầu ra thông qua cửa sổ bật lên.
Vấn đề với đầu vào chuỗi đa dòng trong JavaScript là promptkhông quản lý tốt.

Đối với thuật toán: BFS, nên tìm một giải pháp tối ưu. Tôi giữ một hàng trạng thái trò chơi trong biến l, trạng thái ong trên lưới nhân vật gvà chuỗi di chuyển cho đến nay s. Ngoài ra, có một bộ lưới thu được cho đến nay có thể thay đổi k, để tránh khám phá cùng một lưới nhiều lần.

Vòng lặp chính là

  • dequeue một tình trạng trò chơi
  • thử tất cả các di chuyển có khả năng, ghi lại trạng thái sau mỗi lần di chuyển hợp lệ (IIF lưới kết quả chưa có)
  • nếu tìm thấy giải pháp thì thoát khỏi vòng lặp
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)
}

Chạy Snippet để kiểm tra trong FireFox


1

C ++, 628 byte

Chà, điều này đã không diễn ra rất ngắn:

#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);}}}}

Tôi đã chọn C ++ vì tôi muốn sử dụng các cấu trúc dữ liệu ( set, string), nhưng vốn dĩ nó khá dài dòng. Giải pháp này thực hiện khá tốt về hiệu năng, giải quyết thử nghiệm 2 trong hơn 2 giây trên MacBook Pro, mặc dù nó không được tối ưu hóa cho thời gian chạy.

Mã trước khi bắt đầu loại bỏ khoảng trắng và một số giảm độ dài khác:

#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;
}

Ý tưởng cốt lõi đằng sau thuật toán là hai bộ được duy trì:

  • q là tập hợp các cấu hình đang chờ xử lý.
  • p là tập hợp các cấu hình đã được xử lý.

Thuật toán bắt đầu với cấu hình ban đầu trong q. Trong mỗi bước, một cấu hình được bật lên q, thêm vào p, tất cả các chuyển động con dấu có thể được tạo ra và kết quả là các cấu hình mới được chèn vào q.

Chạy thử nghiệm:

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$

Sử dụng hàng đợi thay vì được đặt cho 'q', bạn có thể tìm thấy các giải pháp ngắn hơn trong thời gian ngắn hơn (giải pháp của tôi cho thử nghiệm 2 là 6 bước).
edc65

@ edc65 Vâng, ban đầu tôi rất ngạc nhiên về số lần di chuyển ở đó. Tôi đã đi qua nó để xác nhận rằng đó là một giải pháp hợp lệ. Sử dụng một FIFO cho qchắc chắn sẽ tốt hơn theo nghĩa đó. Lý do tôi sử dụng một bộ là tôi muốn tránh nhập cùng một mục nhiều lần. Nhưng tôi bắt đầu có những suy nghĩ thứ hai khi tôi thấy kết quả.
Reto Koradi

Hiệu suất khôn ngoan, bạn nên sử dụng hàng đợi VÀ một bộ để tránh lặp lại. Nhưng đặt trong bộ phiên bản sửa đổi của lưới, vì mỗi con dấu con có thể hoán đổi cho nhau.
edc65
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.