ACM 海賊王之偉大航路(深搜剪枝)


“我是要成為海賊王的男人!”

路飛他們偉大航路行程的起點是羅格鎮,終點是拉夫德魯(那里藏匿着“唯一的大秘寶”——ONE PIECE)。而航程中間,則是各式各樣的島嶼。

因為偉大航路上的氣候十分異常,所以來往任意兩個島嶼之間的時間差別很大,從A島到B島可能需要1天,而從B島到A島則可能需要1年。當然,任意兩個島之間的航行時間雖然差別很大,但都是已知的。

現在假設路飛一行從羅格鎮(起點)出發,遍歷偉大航路中間所有的島嶼(但是已經經過的島嶼不能再次經過),最后到達拉夫德魯(終點)。假設他們在島上不作任何的停留,請問,他們最少需要花費多少時間才能到達終點?

輸入輸入數據包含多行。
第一行包含一個整數N(2 < N ≤ 16),代表偉大航路上一共有N個島嶼(包含起點的羅格鎮和終點的拉夫德魯)。其中,起點的編號為1,終點的編號為N。
之后的N行每一行包含N個整數,其中,第i(1 ≤ i ≤ N)行的第j(1 ≤ j ≤ N)個整數代表從第i個島嶼出發到第j個島嶼需要的時間t(0 < t < 10000)。第i行第i個整數為0。輸出輸出為一個整數,代表路飛一行從起點遍歷所有中間島嶼(不重復)之后到達終點所需要的最少的時間。樣例輸入

樣例輸入1:
4
0 10 20 999
5 0 90 30
99 50 0 10
999 1 2 0

樣例輸入2:
5
0 18 13 98 8
89 0 45 78 43 
22 38 0 96 12
68 19 29 0 52
95 83 21 24 0

樣例輸出

樣例輸出1:
100

樣例輸出2:
137

題目如上

如題,第一想法是深搜(據說可以用dp,但還沒想出來),寫了一版深搜,結果超時了,意料之中,下面附代碼以及剪枝。

#include<cstdio> #include<string.h> #include<stdlib.h> #include<math.h> const int inf = 0x3f3f3f3f; int step; int minstep; int a[16][16]; int book[15]; int postion[20]; int n; int npostion[15][1<<15]; int po; void dfs(int cur,int qc) { if(cur+2==n) { step+=a[qc][n-1]; if(step<minstep) minstep=step; step-=a[qc][n-1]; return; } for(int i=1; i<n-1; i++) { if(!book[i])// has been through { if(step>=minstep) continue; if((npostion[i][po+postion[i-1]]<=step+a[qc][i])) continue; po+=postion[i-1]; step+=a[qc][i]; npostion[i][po]=step; book[i]=1; dfs(cur+1,i); po-=postion[i-1]; book[i]=0; step-=a[qc][i]; } } } int main() { for(int i=0; i<14; i++) postion[i]=1<<i; while (~scanf("%d",&n)) { po=0; step=0; minstep=1e17; memset(npostion,inf,sizeof(npostion)); memset(a,0,sizeof(a)); memset(book,0,sizeof(book)); for(int i=0; i<n; i++) for(int j=0; j<n; j++) scanf("%d",&a[i][j]); dfs(0,0); printf("%d\n",minstep); } return 0; }

 

 

  深搜的部分使用cur記錄已經進入的遞歸層,方便在第n-2層(由於起點和終點不算,所以中間可變化的島嶼為n-2個)的時候設置遞歸邊界return。使用book數組進行標記此島嶼是否已經經過。使用變量qc標記上一層遞歸中的i,也就是最后一次確定已經經過的島嶼。

下面為剪枝部分:

由於深搜時會歷經很多不必要的情況,所以需要剪枝操作節省時間花銷,剪枝完成后時間將小於剪枝前的十分之一。

第一步剪枝,也是最容易想到的:在遞歸記錄step的過程中,如果這個step無論是否完成全部島嶼的遍歷,只要其值大於已經存在的不為0的minstep,則舍棄掉當前的情況。

第二步剪枝,利用狀態壓縮的思想,使用po跟蹤記錄此時島嶼的經過狀態,在每一次確認到達此島嶼時記錄以這個島嶼的下標為行標,以已經經過的島嶼為列標的數值,其值為這個狀態下的最小步數,那么在之后的遞歸中,如果出現狀態相同,且其步數比相同狀態下所用步數大的情況下,即可舍棄這步操作,即使用最優的之前已經存在過的狀態情況。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM