Java ( 그로테스크 감소 : 8415 5291 3301)
승인. 기본적으로 아무도 해결책을 제출하지 못했습니다. 며칠 전에 나는이 문제를 해결하기 시작했습니다. . 해당 링크를 따라 GitHub를 통해 진행 상황을 확인하십시오.
편집하다
MT0에 의해 식별 된 수정 된 사이클 체커와 함께 훨씬 더 "골프"된 새로운 솔버 버전. 또한 VM에 사용 가능한 메모리 양을 변경하여 조정할 수있는 빨리 감기 경로를 지원합니다. 최신 BIG 편집 : 몇 가지 다른 작은 인덱스 오류와 조기 최적화가 있음을 깨달았으므로 많은 유형의 승리를 고려하지 못했습니다. 그래서 그것은 조심스럽게 수정되었습니다. 새 버전은 더 작고 아래입니다. 참조 경로의 java -Xmx2GB ZombieHordeMin
경우 트릭을 꽤 잘 수행합니다 (경고는 다소 시간이 걸립니다).
멋진 사실
매혹적인 게도, 길이 24에서 많은 해결책이있다, 내 해결사는 것을 제외하고, MT0의 다른 있지만, 원칙적으로 동일한 일을 찾아 시작 에 연결된 다른 전초 기지를 방문하여 1
. 매혹적인! 인간의 직관에 완전히 반대하지만 완벽하게 유효합니다.
솔루션 하이라이트
여기 내 것이 있습니다. 그것은 (부분적으로) 골프를 쳤으며 b / c는 지수적이고 거의 무차별 솔버입니다. 나는 IDDFS (iterative deepening depth first search) 알고리즘을 사용하므로 건너 뛰지 않는 훌륭한 일반적인 솔버이므로 OP 질문의 두 부분을 모두 해결 합니다 .
- 우승 경로가 발견되면 (무한 좀비) 'x'를 출력합니다.
- 모든 경로가 사망하면 (좀비 좀비), 죽인 최대 수의 좀비를 출력하십시오.
충분한 힘, 메모리 및 시간을 제공하면 느린지도조차도 그렇게 할 것입니다. 이 솔버를 개선하는 데 더 많은 시간을 보냈으며 더 많은 것을 할 수는 있지만 지금은 조금 나아졌습니다. 또한 최고의 무한 좀비 솔루션에 대한 MT0의 조언을 통합하고 이전 버전에서 찾지 못했던 win-checker에서 몇 가지 조기 최적화를 제거했으며 실제로는 설명 된 MT0과 매우 유사한 솔루션을 찾습니다.
다른 몇 가지 주요 내용 :
- 언급했듯이 IDDFS를 사용하여 가능한 가장 짧은 당첨 경로를 찾으십시오.
- 그것은 DFS의 핵심이기 때문에 모든 경로가 영웅의 죽음으로 끝나는지를 발견하고 대부분의 좀비가 사망 한 "최상의"경로를 추적합니다. 영웅을 죽여라!
골프 목적으로 Removed 를 보는 것이 더 재미있게 알고리즘을 계측했습니다 . ungolfed 버전을 보려면 github에 대한 링크 중 하나를 따르십시오.
전체에 걸쳐 많은 의견이 있으므로 내 접근 방식에 따라 자체 솔루션 구축을 위해 다시 구현하거나 어떻게 수행 해야하는지 보여주십시오!
- 메모리 대응 루트 빨리 감기
- 사용 가능한 시스템 메모리까지, 사망을 초래하지 않은 "종료 경로"를 추적합니다.
- 멋진 경로 압축 및 압축 해제 루틴을 사용하면 이전에 방문한 모든 경로를 다시 검색하지 못하도록 IDDFS의 이전 반복 진행 상황이 복원됩니다.
- 의도적 인 사이드 보너스로 막 다른 길로 작동합니다. 막 다른 경로는 저장되지 않으며 향후 IDDFS 깊이에서는 다시 방문하지 않습니다.
솔버의 역사
- 나는 한 단계의 미리보기 알고리즘을 시도했지만 매우 간단한 시나리오에서는 작동하지만 궁극적으로는 평평합니다.
- 그런 다음 2 단계 미리보기 알고리즘을 시도했는데 만족스럽지 못했습니다.
- 그런 다음 n-step lookahead를 구축하기 시작했습니다.이 접근법이 DFS로 환원 될 수 있음을 인식했지만 DFS는 훨씬 우아합니다.
- DFS를 구축하는 동안 IDDFS는 (a) 최고의 HERO (죽음) 경로 또는 (b) 첫 우승주기를 보장 할 수있었습니다.
- 승리주기 검사기를 만드는 것은 쉽지만, 성공적인 검사기에 도달하기 전에 몇 가지 매우 잘못된 반복을 거쳐야했습니다.
- 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
하여 끝 탄약 으로 전 초기 에 도착하십시오.97
- 단계
2
에서 경로 1
를 사용 1
하여 끝 탄약 으로 전 초기 에 도착하십시오.95
- ...
결산 메모
따라서 솔루션을 이기기가 더 어려워지기를 바랍니다.하지만 시도하십시오! 나에게 그것을 사용하고, 병렬 처리, 더 나은 그래프 이론 등을 추가하십시오. 내가 생각하는 몇 가지 사항 이이 접근법을 향상시킬 수 있습니다 .
- 알고리즘이 진행됨에 따라 불필요한 재생을 차단하기 위해 루프를 적극적으로 "감소"합니다.
- 예제 : 예제 문제에서 루프 1-2-3 및 기타 순열을 "한 단계"로 간주하여 더 빨리주기를 종료 할 수 있습니다.
- 예를 들어 노드 1에있는 경우 (a) 2로, (b) 1로, (c) 1-2-3을 한 단계로 진행하는 등의 작업을 수행 할 수 있습니다. 이렇게하면 해를 깊이로 넓게 접을 수있어 특정 깊이에서 경로 수가 증가하지만 긴주기 동안 해결 시간이 크게 단축됩니다.
죽은 경로를 제거하십시오. 현재의 솔루션은 특정 경로가 막 다른 곳에 있다는 것을 "기억"하지 않으며 매번 경로를 다시 검색해야합니다. 죽음이 확실한 경로에서 가장 빠른 순간을 추적하는 것이 좋습니다. 이거 ...
- 조심하면 데드 루트 컬링을 서브 루트 컬로 적용 할 수 있습니다. 예를 들어 1-2-3-4가 항상 사망하고 솔버가 1-3-1-2-3-4 경로를 테스트하려고하는 경우, 해당 경로의 종료를 보장하기 때문에 즉시 해당 경로의 하강을 중지해야합니다 실망에. 신중한 수학으로 킬 수를 계산할 수 있습니다.
시간 동안 메모리를 교환하거나 막 다른 경로를 따르는 것을 피할 수있는 다른 솔루션. 이것도 했어!
1
에서 0 탄약으로 시작합니까? 그래프가 방향이 아닌가?