氷山に取り残された科学者シール


17

前書き

アザラシの家族は、北極圏の氷山に取り残されています。氷山には無線送信機があり、アザラシが助けを求めるために使用できます。ただし、無線送信機の操作方法を知っているのはパパシールだけです。さらに悪いことに、この時期の氷は非常に滑りやすいので、アザラシが別のアザラシにぶつかったり、氷山の端から滑り落ちたりするまで、アザラシが制御不能に滑り、パパアザラシが無線送信機に到達するのが非常に難しくなります。幸いなことに、アザラシの1人はコンピューター科学者であるため、彼女はパパアザラシを無線送信機に操作する方法を理解するためのプログラムを書くことにしました。氷の上にはプログラムを書くスペースがあまりないので、彼女はプログラムで使用するバイトをできるだけ少なくすることにしました。

入力説明

シールのプログラムは、STDIN、コマンドライン引数、またはユーザー入力関数(などraw_input())から入力を受け取ります。変数で事前に初期化することはできません(たとえば、「このプログラムは変数への入力を想定していますx」)。

入力の最初の行は、次の形式の2つのコンマ区切り整数で構成されます。

A,B

その後に、各文字でB構成される行が続きAます。各行には、次の文字のみを含めることができます。

  • .:寒い、寒い、海。マップには常にこれが境界線として表示されます。
  • #:氷山の一部。
  • a... z:氷山のパパシールではないシール。
  • D:パパは氷山にいます。
  • *:無線送信機。

(パパシールは常に大文字で表記されていることに注意してくださいD。小文字dは通常のシールです。)

出力の説明

アザラシの移動方法に関する次の規則に従って、パパのアザラシを無線送信機に移動させるためにアザラシが移動する方法に関する指示のリストを出力します。

  1. ルール:すべてのシールはU、上()、下(D)、左(L)、および右(R)に移動できます。彼らは斜めにスライドすることはできません。
  2. ルール:移動すると、アザラシは別のアザラシと衝突するか海に落ちるまで同じ方向に移動し続けます。
    1. アザラシが別のアザラシと衝突すると、動きを停止します。衝突したシール移動しません
    2. アザラシが海に落ちた場合、それはdrれて地図から消えます。つまり、他のアザラシのコライダーとしては機能せず、再度移動することはできません。
  3. ルール: 2つのシールを同時に動かすことはできません。また、別のシールがまだ動いている間に動かすこともできません。次のシールは、前のシールの移動が停止した後にのみ移動できます。
  4. ルール:シールを複数回移動したり、sealれるシールの数に制限はありません。
  5. ルール:正しい解決策は、パパが無線送信機のを封印することです。パパシールは、滑っている間に送信機を単に通過することはできません。

出力は、それぞれが次の形式の複数の行で構成されます。

A,B

ここで、A(移動するシールであるDパパシールのため、a... z他人のためには)、及びB(いずれかのシールを移動させる方向でありUDL、またはR)。最短ルートを見つける必要がないことに注意してくださいパパがゴールに到達するまでのルートは、許容できる出力です。

入力と出力の例

入力:

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

出力:

D,R

入力:

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

出力(多くの出力のうち1つの出力):

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

入力:

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

出力(多くの出力のうち1つの出力):

v,D
D,L

他にご質問がある場合は、コメントでお尋ねください。


すべての入力に有効なソリューションがありますか?そうでない場合、期待される出力/動作は何ですか?
ジオビット

@Geobitsすべての入力には有効なソリューションがあります。解決策のない入力は無効と見なされ、プログラムはそれらを使用して何でもできます。
アブサン

例外をスローしてプログラムを終了することはできますか?
DLosc

2
パパ以外のシールが無線送信機に当たるとどうなりますか?止まるのか、それを通り抜けるのか?
レトコラディ

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:最後の2行目の先頭に8バイト余分に追加することで、コードの実行を大幅に高速化できます。これk,Lにより、最初のテストケースのように、何も変わらない動きは拒否されます。

ランタイムは、同じ入力であっても、実行ごとに著しく異なります。明らかにset、順序付けされていないタイプであるaを反復処理することで移動する次のシールを選択した結果です。2回目のテストケースの時間を5分30秒に設定しましたが、最初に実行したときにはそれほど長くはなかったようです。上記の最適化では、40秒程度です。


1
興味深いことに、Python 2では毎回同じ順序を与える必要があります。特定のエクスプロイトを回避するために、同じオブジェクトの実行ごとにランダム化されたハッシュを与えるようにPython 3を変更したと思います。「ハッシュランダム化はデフォルトで有効になっています。方法。"
クラウディ

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

スニペットを実行して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);}}}}

データ構造(setstring)を使用したかったため、C ++を選択しましたが、本質的に非常に冗長です。このソリューションは、実行時に最適化されていなくても、MacBook Proでテスト2を2秒強で解決し、パフォーマンスにかなり優れています。

空白やその他の長さの削減を開始する前のコード:

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

アルゴリズムの背後にあるコアアイデアは、2つのセットが維持されるということです。

  • q 処理を保留している構成のセットです。
  • p 処理された構成のセットです。

アルゴリズムは、の初期構成から始まりますq。すべてのステップで、構成がからポップされq、に追加されp、すべての可能なシールの動きが生成され、結果の新しい構成がに挿入されqます。

テスト走行:

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はい、最初はそこに移動した回数に驚きました。それが有効な解決策であることを確認するために私はそれを歩いてみました。qその意味では、FIFOを使用する方が確かに良いでしょう。セットを使用した理由は、同じエントリを複数回入力するのを避けたいからです。しかし、私は結果を見てすぐに考え直し始めました。
レトコラディ

パフォーマンスに関しては、キューとセットを使用して繰り返しを避ける必要があります。ただし、それぞれの赤ちゃんアザラシは交換可能であるため、変更されたバージョンのグリッドをセットに入れます。
edc65
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.