最短Hamilton路徑
阿新 • • 發佈:2020-07-28
題目
思路
前排提示:這裡的下標全部從\(1\)開始
思路怎麼可能時二進位制DP呢?這才第一章啊。
應該吧
然後我用了BFS暴力搜尋。
首先,我們發現題目是一個無向完全圖,雖然我做完還是不知道a[x,y]+a[y,z]>=a[x,z]有什麼用其實這也不重要,我用BFS的管我屁事。好像DP的也無關緊要吧
都是從\(0\)開始那我們就不管了,因為要求每個點都走一遍,所以我們肯定要用某種方式把我們走過的點表示出來,沒錯,就是二進位制,而這裡\(n<=20\),所以二進位制第\(i\)位就代表了這個點有沒有走過,同時也有一個很重要的資訊還要記錄就是我們當前走到了哪個點,不難想出,利用這兩個資訊就足以用這個路徑進行轉移了,什麼,你說有很多條路徑滿足這個資訊?這就是壓縮資訊的精髓了,把一些類似的路徑取一個最小值進行轉移,同時不影響答案(插頭DP其實也是類似的思路)。
仔細想想你會發現,滿足同兩個資訊的一些路徑在轉移上都是一樣的,且最後儘可能是走過的邊權和最小的路徑可以轉移成功,所以只要用邊權和最小的路徑代表這些路徑就行了。
然後對於\(f[i][j]\)(\(i\)記錄路徑,\(j\)表示目前到了哪個點)的轉移就是:
\(f[i+(1<<(k-1))][k]=f[i][j]+a[j][k]\)。
然後就可以愉快的BFS啦。
但是BFS比較容易BFS,如果想要降空間的話還是用DP吧,但是時間複雜度是一樣的,只不過DP利用二進位制的一個性質:走過\(k\)個點的路徑轉移完後,\(k+1\)個點的路徑的\(f\)值就全部出來了,所以只要把含有\(k\)
程式碼
#include<cstdio> #include<cstring> #define N 22 #define M 1100000 using namespace std; int n,a[N][N],f[M][N]/*M表示走過的裝填,N表示目前所在的位置*/; struct qmq { int x,y;/*y表示狀態,x表示位置*/ }list[16000000];int head,tail; inline int mymin(int x,int y){return x<y?x:y;} int main() { scanf("%d",&n); int limit=(1<<n)-1; for(int i=1;i<=limit;i++) { for(int j=1;j<=n;j++)f[i][j]=999999999; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++)scanf("%d",&a[i][j]); } list[1].x=1;list[1].y=1;f[1][1]=0; head=1;tail=1; while(head<=tail) { qmq x=list[head++]; for(int i=1;i<=n;i++) { if((x.y&(1<<(i-1)))==0)//沒走過 { qmq now;now.y=x.y^(1<<(i-1));now.x=i; if(f[now.y][now.x]==999999999)list[++tail]=now;//沒有走過就加入佇列 f[now.y][now.x]=mymin(f[now.y][now.x],f[x.y][x.x]+a[x.x][i]); } } } int ans=f[limit][n]; printf("%d\n",ans); return 0; }