Segel Ilmuwan Terdampar di Gunung Es

17

pengantar

Satu keluarga anjing laut terdampar di atas gunung es di Lingkaran Arktik. Ada pemancar radio yang terletak di gunung es yang bisa digunakan anjing laut untuk meminta bantuan. Namun, hanya segel ayah yang tahu cara mengoperasikan pemancar radio. Dan lebih buruk lagi, es sangat licin sepanjang tahun ini, sehingga segel akan meluncur tak terkendali sampai mereka mengenai segel lain atau meluncur dari tepi gunung es, sehingga sangat sulit bagi segel ayah untuk mencapai pemancar radio. Untungnya, salah satu segel adalah ilmuwan komputer, jadi dia memutuskan untuk menulis sebuah program untuk mencari tahu bagaimana cara mengarahkan segel ayah ke pemancar radio. Karena tidak ada banyak ruang di es untuk menulis sebuah program, ia memutuskan untuk membuat program menggunakan byte sesedikit mungkin.

Deskripsi Input

Program segel akan menerima input dari STDIN, argumen baris perintah, atau fungsi input pengguna (seperti raw_input()). Itu tidak dapat diinisialisasi dalam suatu variabel (mis. "Program ini mengharapkan input dalam suatu variabel x").

Baris pertama input terdiri dari dua bilangan bulat yang dipisahkan koma dalam formulir

A,B

Mengikuti itu adalah Bgaris yang terdiri dari Akarakter masing-masing. Setiap baris hanya dapat berisi karakter dari yang berikut:

  • .: Lautan yang dingin, dingin. Peta akan selalu memiliki ini sebagai perbatasan.
  • #: Bagian dari gunung es.
  • a... z: Segel yang bukan segel ayah di gunung es.
  • D: Segel ayah di gunung es.
  • *: Pemancar radio.

(Perhatikan bahwa segel ayah selalu dinotasikan dengan huruf besar D. Huruf kecil dhanyalah segel biasa.)

Deskripsi Output

Menurut aturan berikut mengenai bagaimana anjing laut dapat bergerak, berikan daftar instruksi untuk anjing laut tentang bagaimana mereka harus pindah untuk mendapatkan segel ayah ke pemancar radio.

  1. Aturan: Semua segel dapat bergerak ke atas ( U), ke bawah ( D), ke kiri ( L), dan ke kanan ( R). Mereka tidak bisa meluncur secara diagonal.
  2. Aturan: Saat bergerak, anjing laut akan terus bergerak ke arah yang sama sampai bertabrakan dengan anjing laut lain atau jatuh ke laut.
    1. Jika segel bertabrakan dengan segel lain, itu akan berhenti bergerak. Segel yang bertabrakan dengannya tidak akan bergerak.
    2. Jika segel jatuh ke laut, itu akan tenggelam dan menghilang dari peta. Artinya, itu tidak bertindak sebagai collider untuk segel lainnya dan tidak bisa dipindahkan lagi.
  3. Aturan: Dua segel tidak bisa bergerak pada saat yang sama, segel juga tidak bisa dipindahkan sementara yang lain masih bergerak. Segel berikutnya hanya dapat dipindahkan setelah segel sebelumnya berhenti bergerak.
  4. Aturan: Tidak ada batasan untuk memindahkan segel beberapa kali, atau jumlah segel yang tenggelam.
  5. Aturan: Solusi yang benar akan memiliki segel ayah berakhir di pemancar radio. Segel ayah tidak bisa begitu saja melewati pemancar saat meluncur.

Output akan terdiri dari beberapa baris, masing-masing dalam bentuk

A,B

Di mana Aadalah segel untuk memindahkan ( Duntuk segel daddy, a... zbagi orang lain), dan Badalah arah untuk memindahkan segel (baik U, D, L, atau R). Perhatikan bahwa Anda tidak perlu menemukan rute terpendek. Rute apa pun yang membawa segel ayah ke tujuan adalah hasil yang dapat diterima.

Contoh Input dan Output

Memasukkan:

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

Keluaran:

D,R

Memasukkan:

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

Output (satu kemungkinan output dari banyak):

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

Memasukkan:

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

Output (satu kemungkinan output dari banyak):

v,D
D,L

Jika Anda memiliki pertanyaan lain, silakan tanyakan di komentar.

Absinth
sumber
Apakah semua input memiliki solusi yang valid? Jika tidak, apa yang diharapkan dari output / perilaku?
Geobits
@Geobits Semua input akan memiliki solusi yang valid. Input tanpa solusi dianggap tidak valid dan program Anda dapat melakukan apa pun dengannya.
absinth
Apakah diperbolehkan mengakhiri program dengan melemparkan pengecualian?
DLosc
2
Apa yang terjadi jika segel non-ayah menyentuh pemancar radio? Apakah itu akan berhenti, atau bergerak melewatinya?
Reto Koradi
1
Itu membatalkan solusi saya. :(
DLosc

Jawaban:

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

Saya dapat melakukan penjelasan yang lebih terperinci nanti jika orang menginginkannya, tetapi pada dasarnya ini menjalankan pencarian mendalam-pertama dengan memperdalam berulang-ulang pada ruang keadaan kemungkinan pergerakan. Jika pindah menyebabkan segel ayah jatuh, itu segera ditolak. Jika ayah berakhir di sebelah pemancar, urutan gerakan dicetak, dan program dibagi dengan nol untuk memaksa keluar.

Saya dapat membuat kode berjalan lebih cepat secara signifikan dengan menambahkan if G!=g:di awal baris kedua-terakhir, untuk 8 byte tambahan - ini menolak gerakan yang tidak mengubah apa pun, seperti k,Ldalam kasus uji pertama.

Runtime bervariasi bervariasi dari satu run ke yang berikutnya, bahkan dengan input yang sama - jelas merupakan hasil dari fakta bahwa saya memilih segel berikutnya untuk bergerak dengan mengulangi lebih dari satu set, yang merupakan tipe unordered. Saya menghitung waktu test case kedua pada 5 menit 30 detik, meskipun sepertinya tidak begitu lama pertama kali saya menjalankannya. Dengan optimasi yang disebutkan di atas, ini lebih seperti 40 detik.

DLosc
sumber
1
Menariknya di Python 2 itu harus memberikan urutan yang sama setiap kali. Saya pikir mereka mengubah Python 3 untuk memberikan hash acak pada setiap run untuk objek yang sama untuk menghindari eksploitasi tertentu: "Pengacakan hash diaktifkan secara default. Atur variabel lingkungan PYTHONHASHSEED menjadi 0 untuk menonaktifkan pengacakan hash. Lihat juga objek .__ hash __ () metode."
Claudiu
4

JavaScript (ES6) 322 334 323

Edit2 Menambahkan animasi di cuplikan

Edit perbaikan Bug, ingat posisi awal '*', jadi saya menemukannya bahkan ketika segel meluncur di atasnya dan menghapusnya.

Diimplementasikan sebagai fungsi dengan string input sebagai parameter (mungkin tidak valid tetapi menunggu klarifikasi). Keluaran melalui popup.
Masalah dengan input string multiline dalam JavaScript adalah bahwa prompttidak mengelolanya dengan baik.

Adapun algoritma: BFS, harus menemukan solusi optimal. Saya menyimpan antrian status permainan dalam variabel l, status di atas kisi karakter gdan urutan gerakan sejauh ini s. Selain itu, ada satu set kisi yang diperoleh sejauh ini dalam variabel k, untuk menghindari menjelajahi kisi yang sama berulang kali.

Loop utama adalah

  • dequeue status game
  • coba semua kemungkinan gerakan, enqueue status setelah setiap gerakan yang valid (IIF kisi-kisi yang dihasilkan belum ada)
  • jika solusi ditemukan maka keluar dari loop
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)
}

Jalankan Cuplikan untuk menguji di FireFox

edc65
sumber
1

C ++, 628 byte

Ya, ini tidak terlalu pendek:

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

Saya memilih C ++ karena saya ingin menggunakan struktur data ( set, string), tetapi pada dasarnya cukup bertele-tele. Solusinya tidak cukup baik pada kinerja, menyelesaikan tes 2 dalam lebih dari 2 detik pada MacBook Pro, meskipun tidak dioptimalkan untuk runtime.

Kode sebelum mulai menghilangkan spasi putih dan beberapa pengurangan panjang lainnya:

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

Gagasan inti di balik algoritma ini adalah bahwa dua set dipertahankan:

  • q adalah serangkaian konfigurasi yang sedang diproses pemrosesan.
  • p adalah sekumpulan konfigurasi yang telah diproses.

Algoritme dimulai dengan konfigurasi awal pada q. Di setiap langkah, konfigurasi muncul dari q, ditambahkan ke p, semua gerakan segel yang mungkin dihasilkan, dan konfigurasi baru yang dihasilkan dimasukkan ke dalam q.

Uji coba:

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$
Reto Koradi
sumber
Menggunakan antrian alih-alih ditetapkan untuk 'q', Anda dapat menemukan solusi yang lebih pendek dalam waktu yang lebih singkat (solusi saya untuk pengujian 2 adalah 6 langkah).
edc65
@ edc65 Ya, saya awalnya terkejut dengan jumlah gerakan di sana. Saya memang berjalan melaluinya untuk mengkonfirmasi bahwa itu adalah solusi yang valid. Menggunakan FIFO untuk qtentu akan lebih baik dalam arti itu. Alasan saya menggunakan set adalah karena saya ingin menghindari memasukkan entri yang sama beberapa kali. Tapi saya mulai berpikir dua kali setelah melihat hasilnya.
Reto Koradi
Dari segi kinerja, Anda harus menggunakan antrian DAN satu set untuk menghindari pengulangan. Tetapi pasang set versi grid yang dimodifikasi, karena setiap segel bayi dapat dipertukarkan.
edc65