題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=3001
附上一個寫的 比較容易理解的連結:http://blog.sina.com.cn/s/blog_6ec19c780100ydws.html
題意:給定幾個城市間的旅遊費用,求把每個城市至少遍曆一遍,且至多兩遍的情況下的至少花費。否則輸出-1。
代碼寫出來之後,會發現和二進位狀壓Dp差不多,只是多了一個附加條件,每個點可以重複一次,所以得用三進位狀態表示,由於電腦中儲存方式不是三進位,所以我們需要預先處理出每種狀態的三進位表示形式,便於枚舉枚舉每個位置的狀態。這就是主要與二進位不同的地方,二進位是直接利用電腦位元運算擷取狀態,相對來說,少了這一過程。
代碼:
<span style="font-size:18px;">#include<iostream>#include<sstream>#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>#include<set>#include<map>#include<queue>#include<vector>#include<string>#define LL long long#define INF 0x7fffffffusing namespace std;int n,m,Map[12][12];int bit[11] = {1,3,9,27,81,243,729,2187,6561,19683,59049};int dp[60000][10],num[60000][11];void init(){//擷取每種狀態的三進位表示形式 for(int i=0;i<bit[10];i++){ int t=i; for(int j=0;j<10;j++) num[i][j]=t%3,t/=3; }}void Dp(){ memset(dp,-1,sizeof(dp));//初始化為-1 for(int i=0;i<n;i++)//把每個起點的費用初始化為0 dp[bit[i]][i]=0; int ans=-1; for(int i=0;i<bit[n];i++){//枚舉每一種狀態 int op=1;//所有點都至少遍曆過一次的標誌 for(int j=0;j<n;j++){//和二進位一樣,枚舉每個可以作為起點(即跳轉)的點 if(num[i][j]==0) op=0; if(dp[i][j]==-1 || num[i][j]==0) continue;//不能跳轉 for(int k=0;k<n;k++){//枚舉可以走的下個點(跳轉目標點) if(Map[j+1][k+1]==-1 || k==j || num[i][k]>=2) continue;//下一個點不合法 int s=i+bit[k]; if(dp[s][k]==-1) dp[s][k]=dp[i][j]+Map[j+1][k+1]; else dp[s][k]=min(dp[s][k],dp[i][j]+Map[j+1][k+1]); } } if(op){//如果每個點至少遍曆過一次,更新最小費用 for(int j=0;j<n;j++) if(dp[i][j]!=-1) ans=(ans==-1)? dp[i][j]:min(ans,dp[i][j]); } } cout<<ans<<endl;}int main(){ //freopen("D:\\in.txt","r",stdin); init(); while(cin>>n){ cin>>m; int a,b,c; memset(Map,-1,sizeof(Map)); for(int i=0;i<m;i++){ cin>>a>>b>>c; if(Map[a][b]==-1) Map[a][b]=Map[b][a]=c; else Map[a][b]=Map[b][a]=min(Map[a][b],c); } Dp(); } return 0;}</span>