Java(グロテスクでない:8415 5291 3301)
OK。基本的に、誰も解決策を提出していないのは恥ずかしいです。だから数日前、私はこの問題を解決しようとし始めました、それは素晴らしいことです。。そのリンクをたどって、GitHubで進捗状況を確認してください。
編集
MT0によって識別される修正されたサイクルチェッカーを備えた、新しい「ゴルフ」バージョンのソルバー。また、VMで使用可能なメモリ量を変更することで調整可能な、高速転送ルートもサポートしています。最新のBIG編集:他にもいくつかの小さなインデックスエラーと時期尚早な最適化があり、非常に多くの種類の勝利を検討できなかったことに気付きました。だから、それは慎重に修正されました。新しいバージョンはより小さく、以下の両方です。参照ルートでjava -Xmx2GB ZombieHordeMinは、このトリックは非常にうまく機能します(注意してください、しばらく時間がかかります)。
クールなファクトイド
魅力的なねじれでは、長さが24で多くのソリューションがあり、そして私のソルバーは、それがあることを除いて、MT0の異なるが、原則的に同じものを見つけ始まりに接続された他の前哨基地を訪問して1。魅力的!人間の直感に完全に対抗しますが、完全に有効です。
ソリューションのハイライト
これが私のものです。それは(部分的に)ゴルフされ、b / cそれは指数関数的で、ほとんど力にならないソルバーです。IDDFS(反復深化深さ優先検索)アルゴリズムを使用しているため、スキップしない優れた一般的なソルバーであるため、OPの質問の両方の部分を解決します。
- 勝ちのルートが見つかった場合(無限のゾンビ)、「x」を出力します。
- すべてのルートが死んでいる場合(有限ゾンビ)、殺されたゾンビの最大数を出力します。
十分なパワー、メモリ、および時間を与えてください。そうすれば、スローデスマップであってもそれができます。私はこのソルバーを改善するためにもう少し時間を費やしましたが、もっとできることはありますが、今は少し良くなっています。また、最高の無限ゾンビソリューションに関するMT0のアドバイスを統合し、以前のバージョンがそれを見つけるのを妨げていたwin-checkerからいくつかの時期尚早な最適化を削除しました。
他のいくつかのハイライト:
- 前述のように、IDDFSを使用して、可能な限り最短のルートを見つけます。
- それはDFSの中核であるため、すべてのルートがヒーローの死で終わるかどうかも発見し、殺されたほとんどのゾンビに関して「最適な」ルートを追跡します。ヒーローを死にます!
私はアルゴリズムをインストルメント化して、ゴルフの目的でRemoved を見るのをより面白くしました。GitHubへのリンクのいずれかをたどって、未使用バージョンを確認してください。
全体にわたって多くのコメントがありますので、私のアプローチに基づいて独自のソリューションを再実装したり、どのように行うべきかを教えてください!
- メモリ対応ルートの早送り
- 使用可能なシステムメモリまで、死に至らなかった「終了ルート」を追跡します。
- 高度なルート圧縮および圧縮解除ルーチンを使用して、IDDFSの以前の反復からの進捗が復元され、以前にアクセスしたすべてのルートが再検出されないようにします。
- 意図的な副ボーナスとして、行き止まりのルートカリングとして機能します。行き止まりのルートは保存されず、IDDFSの将来の深さで再びアクセスされることはありません。
ソルバーの歴史
- ワンステップ先読みアルゴリズムの束を試しましたが、非常に単純なシナリオでは機能しますが、最終的には平坦になります。
- 次に、2段階の先読みアルゴリズムを試してみましたが、満足できませんでした。
- その後、このアプローチがDFSに還元可能であると認識したときに、nステップ先読みの構築を開始しましたが、DFSははるかにエレガントです。
- DFSの構築中に、IDDFSが(a)最良のHERO(死亡)ルートを見つけるか、(b)最初の勝利サイクルを確保することに気付きました。
- win-cycleチェッカーの作成は簡単ですが、成功するチェッカーにたどり着くまでに、非常に間違った繰り返しを何度か実行する必要がありました。
- MT0のwin-pathを考慮に入れて、3行の早すぎる最適化を削除して、アルゴリズムを盲目にしました。
- IDDFS呼び出し間の不要な作業のやり直しを防ぐために、指定したすべてのメモリを使用する適応型のルートキャッシングアルゴリズムを追加し、メモリの限界まで行き止まりのルートをカリングします。
(ゴルフ)コード
コードに進みます(ここまたはここで ungolfedバージョンを取得します):
import java.util.*;public class ZombieHordeMin{int a=100,b,m,n,i,j,z,y,D=0,R,Z,N;int p[][][];Scanner in;Runtime rt;int[][]r;int pp;int dd;int[][]bdr;int ww;int[][]bwr;int[][]faf;int ff;boolean ffOn;public static void main(String[]a){(new ZombieHordeMin()).pR();}ZombieHordeMin(){in=new Scanner(System.in);rt=Runtime.getRuntime();m=in.nextInt();N=in.nextInt();p=new int[m+1][m+1][N+1];int[]o=new int[m+1];for(b=0;b<N;b++){i=in.nextInt();j=in.nextInt();z=in.nextInt();o[i]++;o[j]++;D=(o[i]>D?o[i]:D);p[i][j][++p[i][j][0]]=z;if(i!=j)p[j][i][++p[j][i][0]]=z;D=(o[j]>D?o[j]:D);}m++;}void pR(){r=new int[5000][m+3];r[0][0]=a;Arrays.fill(r[0],1,m,1);r[0][m]=1;r[0][m+1]=0;r[0][m+2]=0;ww=-1;pp=dd=0;pR(5000);}void pR(int aMD){faf=new int[D][];ff=0;ffOn=true;for(int mD=1;mD<=aMD;mD++){System.out.printf("Checking len %d\n",mD);int k=ffR(0,mD);if(ww>-1){System.out.printf("%d x\n",ww+1);for(int win=0;win<=ww;win++)System.out.printf(" %d:%d,%d-%d",win,bwr[win][0],bwr[win][1],bwr[win][2]);System.out.println();break;}if(k>0){System.out.printf("dead max %d kills, %d steps\n",pp,dd+1);for(int die=0;die<=dd;die++)System.out.printf(" %d:%d,%d-%d",die,bdr[die][0],bdr[die][1],bdr[die][2]);System.out.println();break;}}}int ffR(int dP,int mD){if(ff==0)return pR(dP,mD);int kk=0;int fm=ff;if(ffOn&&D*fm>rt.maxMemory()/(faf[0][0]*8+12))ffOn=false;int[][]fmv=faf;if(ffOn){faf=new int[D*fm][];ff=0;}for(int df=0;df<fm;df++){dS(fmv[df]);kk+=pR(fmv[df][0],mD);}fmv=null;rt.gc();return kk==fm?1:0;}int pR(int dP,int mD){if(dP==mD)return 0;int rT=0;int dC=0;int src=r[dP][m];int sa=r[dP][0];for(int dt=1;dt<m;dt++){for(int rut=1;rut<=p[src][dt][0];rut++){rT++;r[dP+1][0]=sa-p[src][dt][rut]+r[dP][dt];for(int cp=1;cp<m;cp++)r[dP+1][cp]=(dt==cp?1:r[dP][cp]+1);r[dP+1][m]=dt;r[dP+1][m+1]=rut;r[dP+1][m+2]=r[dP][m+2]+p[src][dt][rut];if(sa-p[src][dt][rut]<1){dC++;if(pp<r[dP][m+2]+sa){pp=r[dP][m+2]+sa;dd=dP+1;bdr=new int[dP+2][3];for(int cp=0;cp<=dP+1;cp++){bdr[cp][0]=r[cp][m];bdr[cp][1]=r[cp][m+1];bdr[cp][2]=r[cp][0];}}}else{for(int chk=0;chk<=dP;chk++){if(r[chk][m]==dt){int fR=chk+1;for(int cM=0;cM<m+3;cM++)r[dP+2][cM]=r[dP+1][cM];for(;fR<=dP+1;fR++){r[dP+2][0]=r[dP+2][0]-p[r[dP+2][m]][r[fR][m]][r[fR][m+1]]+r[dP+2][r[fR][m]];for(int cp=1;cp<m;cp++)r[dP+2][cp]=(r[fR][m]==cp?1:r[dP+2][cp]+1);r[dP+2][m+2]=r[dP+2][m+2]+p[r[dP+2][m]][r[fR][m]][r[fR][m+1]];r[dP+2][m]=r[fR][m];r[dP+2][m+1]=r[fR][m+1];}if(fR==dP+2&&r[dP+2][0]>=r[dP+1][0]){ww=dP+1;bwr=new int[dP+2][3];for(int cp=0;cp<dP+2;cp++){bwr[cp][0]=r[cp][m];bwr[cp][1]=r[cp][m+1];bwr[cp][2]=r[cp][0];}return 0;}}}dC+=pR(dP+1,mD);if(ww>-1)return 0;}for(int cp=0;cp<m+3;cp++)r[dP+1][cp]=0;}}if(rT==dC)return 1;else{if(ffOn&&dP==mD-1)faf[ff++]=cP(dP);return 0;}}int[]cP(int dP){int[]cmp=new int[dP*2+3];cmp[0]=dP;cmp[dP*2+1]=r[dP][0];cmp[dP*2+2]=r[dP][m+2];for(int zip=1;zip<=dP;zip++){cmp[zip]=r[zip][m];cmp[dP+zip]=r[zip][m+1];}return cmp;}void dS(int[]cmp){int[]lv=new int[m];int dP=cmp[0];r[dP][0]=cmp[dP*2+1];r[dP][m+2]=cmp[dP*2+2];r[0][0]=100;r[0][m]=1;for(int dp=1;dp<=dP;dp++){r[dp][m]=cmp[dp];r[dp][m+1]=cmp[dP+dp];r[dp-1][cmp[dp]]=dp-lv[cmp[dp]];r[dp][m+2]=r[dp-1][m+2]+p[r[dp-1][m]][cmp[dp]][cmp[dP+dp]];r[dp][0]=r[dp-1][0]+r[dp-1][cmp[dp]]-p[r[dp-1][m]][cmp[dp]][cmp[dP+dp]];lv[cmp[dp]]=dp;}for(int am=1;am<m;am++)r[dP][am]=(am==cmp[dP]?1:dP-lv[am]+1);}}
ここで githubからコードを取得して、変更を追跡します。ここに私が使用した他のいくつかのマップがあります。
出力例
参照ソリューションの出力例:
$ java -d64 -Xmx3G ZombieHordeMin > reference_route_corrected_min.out
5 6 1 2 4 2 3 4 3 1 4 2 4 10 2 5 10 1 1 50
Checking len 1
Checking len 2
Checking len 3
Checking len 4
Checking len 5
Checking len 6
Checking len 7
Checking len 8
Checking len 9
Checking len 10
Checking len 11
Checking len 12
Checking len 13
Checking len 14
Checking len 15
Checking len 16
Checking len 17
Checking len 18
Checking len 19
Checking len 20
Checking len 21
Checking len 22
Checking len 23
Checking len 24
25 x
0:1,0-100 1:3,1-97 2:1,1-95 3:2,1-94 4:5,1-88 5:2,1-80 6:4,1-76 7:2,1-68 8:1,1-70 9:2,1-68 10:1,1-66 11:2,1-64 12:1,1-62 13:2,1-60 14:1,1-58 15:2,1-56 16:1,1-54 17:2,1-52 18:1,1-50 19:2,1-48 20:1,1-46 21:2,1-44 22:1,1-42 23:2,1-40 24:1,1-38
次のようなルート出力を読み取りますstep::source、route-to-get-here- ammo。したがって、上記のソリューションでは、次のように読み取ります。
- ステップ
0、前哨基地の1弾薬を持ちます100。
- ステップで
1、ルート1を使用して、3弾薬を終了する前post 基地に到達します97
- ステップで
2、ルート1を使用して、1弾薬を終了する前post 基地に到達します95
- ...
最後に
だから、私は私のソリューションを打ち負かすのを難しくしたことを願っていますが、試してください!私に対してそれを使用し、いくつかの並列処理、より良いグラフ理論などを追加します。私が考えているいくつかのことがこのアプローチを改善する可能性があります。
- アルゴリズムが進行するにつれて、ループを積極的に「削減」して、不必要なリトレッドをカットします。
- 例:問題の例では、ループ1-2-3およびその他の順列を「1ステップ」とみなし、より早くサイクルを終了できるようにします。
- たとえば、ノード1にいる場合は、(a)2に進む、(b)1に進む、(c)1つのステップとして1-2-3に進むなどのいずれかを実行できます。これにより、解決された深さを幅に折り畳むことができ、特定の深さでルートの数が増えますが、長いサイクルの解決までの時間を大幅に短縮できます。
死んだルートを間引く。私の現在のソリューションは、特定のルートが行き止まりであることを「覚えていない」ため、毎回それを再発見する必要があります。死が確実であり、それを超えて進まないルートで最も早い瞬間を追跡する方が良いでしょう。これをやった...
- 注意すれば、デッドルートカリングをサブルートカリングとして適用できます。たとえば、1-2-3-4が常に死に至り、ソルバーがルート1-3-1-2-3-4をテストしようとしている場合、終了が保証されるため、そのパスの下降を直ちに停止する必要があります。がっかりしました。いくつかの注意深い数学で、キル数を計算することはまだ可能です。
時間とメモリを交換するか、行き止まりのルートを積極的に回避できる他のソリューション。これもやった!
1弾薬数0から始まりますか?グラフは無指向性ですか?