Java ( ผิดปกติน้อยกว่า : 8415 5291 3301)
ตกลง. โดยพื้นฐานแล้วฉันอายที่ไม่มีใครส่งวิธีแก้ปัญหา เมื่อไม่กี่วันที่ผ่านมาฉันเริ่มพยายามแก้ปัญหานี้มันยอดเยี่ยมมาก . ติดตามลิงก์นั้นเพื่อดูความคืบหน้าของฉันผ่าน GitHub
แก้ไข
เวอร์ชั่นตัวแก้ปัญหาใหม่ "golfed" ที่มากขึ้นพร้อมตัวตรวจสอบรอบที่แก้ไขตามที่ระบุโดย MT0 นอกจากนี้ยังรองรับเส้นทางการส่งต่อที่รวดเร็วปรับแต่งได้โดยการเปลี่ยนจำนวนหน่วยความจำที่มีให้กับ VM ล่าสุดBIGแก้ไข: ตระหนักว่าผมมีข้อผิดพลาดเล็ก ๆ ไม่กี่ดัชนีและการเพิ่มประสิทธิภาพก่อนวัยอันควรที่ส่งผลให้เกิดความล้มเหลวในการพิจารณาค่อนข้างเป็นจำนวนมากของประเภทของผู้ชนะ นั่นคือการแก้ไขอย่างระมัดระวัง เวอร์ชันใหม่มีทั้งขนาดเล็กและด้านล่าง สำหรับเส้นทางอ้างอิงของเราjava -Xmx2GB ZombieHordeMin
ทำกลอุบายค่อนข้างดี (ได้รับคำเตือนมันจะใช้เวลาสักครู่)
เจ๋ง
ในการบิดที่น่าสนใจมีหลายโซลูชั่นที่มีความยาว 24 และแก้ของฉันพบหนึ่งที่แตกต่างจาก MT0 แต่ที่เหมือนกันในหลักการยกเว้นว่ามันเริ่มต้นโดยการเยี่ยมชมนายทวารอื่น ๆ 1
ที่เชื่อมต่อกับ ที่น่าสนใจ! ทั้งหมดเคาน์เตอร์สัญชาตญาณของมนุษย์ แต่ที่ถูกต้องสมบูรณ์
โซลูชั่นไฮไลท์
ดังนั้นนี่คือเหมือง มันคือกอล์ฟบางส่วน, b / c เป็นตัวแก้ปัญหาแบบเลขชี้กำลัง, กำลังเกือบจะดุร้าย ฉันใช้อัลกอริทึม IDDFS (การค้นหาความลึกครั้งแรกซ้ำลึก) ซ้ำดังนั้นจึงเป็นนักแก้ปัญหาทั่วไปที่ยอดเยี่ยมที่ไม่ข้ามดังนั้นจึงสามารถแก้ปัญหาทั้งสองส่วนของคำถาม OP:
- หากพบเส้นทางที่ชนะ (ซอมบี้ไม่มีที่สิ้นสุด) เอาท์พุท 'x'
- หากเส้นทางทั้งหมดจบลงด้วยความตาย (ซอมบี้ที่ จำกัด ) ให้ฆ่าซอมบี้จำนวนมากที่สุด
ให้พลังความทรงจำและเวลาเพียงพอและจะทำเช่นนั้นแม้กระทั่งแผนที่ความตายช้า ฉันใช้เวลาในการปรับปรุงตัวแก้ปัญหานี้เพิ่มขึ้นและในขณะที่สามารถทำได้มากกว่านี้ ฉันยังได้รวมคำแนะนำของ MT0 ในการแก้ปัญหาซอมบี้อนันต์ที่ดีที่สุดและลบการปรับให้เหมาะสมก่อนกำหนดจำนวนมากออกจากตัวตรวจสอบของฉันที่ป้องกันเวอร์ชันก่อนหน้าจากการค้นหามันและตอนนี้ฉันจริง ๆ
ไฮไลท์อื่น ๆ :
- ตามที่กล่าวไว้ใช้ IDDFS เพื่อค้นหาเส้นทางการชนะที่สั้นที่สุด
- เนื่องจากเป็นแกนหลักของ DFS มันจะค้นพบว่าทุกเส้นทางจบลงด้วยการตายของฮีโร่ของเราและติดตามเส้นทาง "ดีที่สุด" ในแง่ของซอมบี้ที่ถูกฆ่าตาย ตายวีรบุรุษ!
ฉันได้นำอัลกอริทึมมาใช้เพื่อทำให้นาฬิกาน่าสนใจยิ่งขึ้นสำหรับการตีกอล์ฟ ติดตามลิงค์ใดลิงก์หนึ่งเพื่อไปยัง GitHub เพื่อดูเวอร์ชั่นที่ไม่ดี
มีความคิดเห็นจำนวนมากเช่นกันดังนั้นอย่าลังเลที่จะนำไปใช้ใหม่สำหรับโซลูชันของคุณในการสร้างแนวทางหรือแสดงให้ฉันเห็นว่าควรทำอย่างไร!
- การส่งต่ออย่างรวดเร็วของหน่วยความจำที่ปรับเปลี่ยนได้
- ถึงหน่วยความจำระบบที่มีอยู่จะติดตาม "เส้นทางปลายทาง" ที่ไม่ส่งผลให้เสียชีวิต
- การใช้การบีบอัดเส้นทางแฟนซีและรูทีนการบีบอัดความคืบหน้าจากการวนซ้ำก่อนหน้าของ IDDFS จะถูกกู้คืนเพื่อป้องกันการค้นพบเส้นทางที่เยี่ยมชมก่อนหน้านี้ทั้งหมด
- ในฐานะโบนัสด้านเจตนาทำหน้าที่เป็นตัวเลือกเส้นทางที่สิ้นตาย เส้นทางปลายทางไม่ถูกจัดเก็บและจะไม่ถูกเยี่ยมชมอีกครั้งในส่วนลึกของ IDDFS ในอนาคต
ประวัติความเป็นมาของนักแก้ปัญหา
- ฉันลองใช้อัลกอริทึมการค้นหาล่วงหน้าแบบขั้นตอนเดียวและในขณะที่สำหรับสถานการณ์ที่ง่ายมาก ๆ พวกเขาจะทำงานในที่สุดพวกเขาก็ล้มเหลว
- จากนั้นฉันลองอัลกอริธึมการค้นหาล่วงหน้าสองขั้นตอนซึ่ง .. ไม่พอใจ
- จากนั้นฉันก็เริ่มสร้าง lookahead แบบขั้นตอนเดียวเมื่อฉันรู้ว่าวิธีนี้ลดทอนลงไปที่ DFS แต่ DFS นั้น ... สง่างามกว่ามาก
- ในขณะที่สร้าง DFS เกิดขึ้นกับฉันที่ IDDFS จะทำให้แน่ใจว่า (a) ค้นหาเส้นทาง HERO (death) ที่ดีที่สุดหรือ (b) รอบการชนะครั้งแรก
- กลายเป็นว่าการสร้างตัวตรวจสอบรอบชนะนั้นเป็นเรื่องง่าย แต่ฉันต้องผ่านการทำซ้ำหลาย ๆ ครั้งก่อนที่จะถึงตัวตรวจสอบที่ประสบความสำเร็จ
- เอาไว้ใน win-path ของ MT0 เพื่อลบการเพิ่มประสิทธิภาพก่อนกำหนดสามบรรทัด
- เพิ่มอัลกอริธึมการแคชเส้นทางที่ปรับได้ซึ่งจะใช้หน่วยความจำทั้งหมดที่คุณให้ไว้เพื่อป้องกันการทำซ้ำที่ไม่จำเป็นระหว่างการโทร 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 ammo หรือไม่? กราฟเป็นแบบสองทิศทางหรือไม่?