3進位制狀態壓縮DP——HDU3001 Travelling 旅行商問題
阿新 • • 發佈:2019-02-13
題目大意:n個城市之間有m條路,經過每條路徑都有一定的花費,當然有些城市是不可達的。
遍歷所有的城市,並且每個城市最多隻能遍歷2次,求取花費的最小費用並輸出,如果找不到這樣的路徑,輸出-1。(n<=10)
解題思路:由於n是小於10的,所以這個題目可以使用狀態壓縮來做。由於每個城市最多可以遍歷2次,所以需要採用3進位制的狀態壓縮。
基本狀態轉移方程:dp[l][k]=min(dp[l][k],dp[i][j]+Map[j][k])
其中dp[i][j]表示當前狀態是i,當前點是j點。
由狀態方程可知,我們只需要遍歷所有的狀態i,以及所有當前點j和所有的下一個點k就可以了。
原始碼:
//最多遍歷的城市只有10,個那麼每一行的狀態是非常小的,所以可以使用狀態壓縮DP //經典的旅行商問題,每個城市最多走2遍至少走1遍,求最小遍歷費用 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<math.h> #include<set> #include<map> #include<vector> #define INF 0x3f3f3f3f //不可達狀態 #include<algorithm> using namespace std; int n,m; //n個城市,m條路,最多10個城市 int Map[11][11]; int dp[60000][11]; //狀態為i時並且這個時候正處在j點的時候的最小費用 int Time[60000][11]; //狀態為i時遍歷j點的次數 int s[11]; void init() { int i,j,t; s[0]=1; for(i=1;i<=n;i++) { s[i]=s[i-1]*3; //求出到達每個點狀態的增量 } for(i=0;i<s[n];i++) { t=i; for(j=0;j<n;j++) { Time[i][j]=t%3; //求取當狀態為i的時候遍歷j點的次數 t=t/3; } } return; } void DP() { int i,j,t,k,l,ans,flag; ans=INF; for(i=0;i<s[n];i++) //列舉每種狀態 { flag=1; for(j=0;j<n;j++) //列舉當前點j { if(Time[i][j]==0) {flag=0; continue;} //狀態i不包含j點 if(dp[i][j]==INF) continue; //說明該狀態是不存在的 for(k=0;k<n;k++) //列舉下一個點k { if(Map[j][k]==INF) continue; //兩點不可達 if(Time[i][k]==2 || j==k) continue; //k點已經被遍歷了2次 l=i+s[k]; dp[l][k]=min(dp[l][k],dp[i][j]+Map[j][k]); } } if(flag) //這個條件表示i狀態包含了所有的點 { for(j=0;j<n;j++) { ans=min(ans,dp[i][j]); } } } if(ans==INF) printf("-1\n"); else printf("%d\n",ans); return; } int main() { int i,j,k,t; //freopen("in.txt","r",stdin); while(scanf("%d%d",&n,&m)==2) { memset(dp,INF,sizeof(dp)); memset(Map,INF,sizeof(Map)); memset(Time,0,sizeof(Time)); while(m--) { scanf("%d%d%d",&i,&j,&k); i--,j--; //注意:!!城市是從0開始計數 Map[i][j]=Map[j][i]=min(k,Map[i][j]); } init(); for(i=0;i<n;i++) //列舉起點 { dp[s[i]][i]=0; //初始化起點的耗費 } DP(); } return 0; }