HDU 3001 Travelling(状态压缩DP+三进制)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3001

题目大意:有n个城市,m条路,每条路都有一定的花费,可以从任意城市出发,每个城市不能经过两次以上,要求经过所有城市并且花费最少,求出最小花费。

解题思路:三进制的状态压缩DP,跟二进制还是有一点不一样的,因为三进制没有直接的位运算,还要自己先做处理利用num[i][j]记录数字i各位的三进制表示方便计算,其他的就跟二进制状态压缩没有太大区别了。还有注意:

     ①开始要将n个起点初始化,dp[bit[i]][i]=0

     ②有重边,要去重

代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<queue>
 5 #include<cstring>
 6 using namespace std;
 7 const int INF=0x3f3f3f3f;
 8 const int N=6e4+5;
 9 
10 int n,m,ans;
11 int map[15][15];
12 int dp[N][15];
13 int bit[15]={1,3,9,27,81,243,729,2187,6561,19683,59049};
14 int num[N][15];
15 
16 //计算所有数字10位以内三进制表示 
17 void make_tab(){
18     for(int i=0;i<bit[10];i++){
19         int b=i;
20         for(int j=0;j<10;j++){
21             num[i][j]=b%3;
22             b/=3;
23         }
24     }
25 }
26 
27 int main(){
28     make_tab(); 
29     while(~scanf("%d%d",&n,&m)){
30         memset(map,0x3f,sizeof(map));
31         memset(dp,0x3f,sizeof(dp));
32         int ans=INF; 
33         for(int i=1;i<=m;i++){
34             int a,b,c;
35             scanf("%d%d%d",&a,&b,&c);
36             a--,b--;
37             //去重边 
38             map[a][b]=min(map[a][b],c);
39             map[b][a]=min(map[b][a],c);
40         }
41         for(int i=0;i<n;i++)
42             dp[bit[i]][i]=0;//对每个点定位初始点0
43         
44         for(int i=0;i<bit[n];i++){
45             bool flag=true;//表示所有位都是>=1,也就是每个城镇都走过了 
46             for(int j=0;j<n;j++){
47                 if(num[i][j]==0)
48                     flag=false;
49                 if(dp[i][j]==INF)//判断是否走到j点 
50                     continue;
51                 for(int k=0;k<n;k++){
52                     //注意这个num[i][k]>=2,因为如果i状态在k点已经走过两次了显然是不能继续往下走的 
53                     if(j==k||num[i][k]>=2||map[j][k]==INF)
54                         continue;
55                     int next=i+bit[k];//从j点走到k点 
56                     dp[next][k]=min(dp[next][k],dp[i][j]+map[j][k]); 
57                 } 
58             }
59             //如果所有城镇都走了一遍则可以找出最小值 
60             if(flag){
61                 for(int j=0;j<n;j++)
62                     ans=min(ans,dp[i][j]);
63             }
64         }
65         if(ans==INF)
66             puts("-1");
67         else
68             printf("%d\n",ans);
69     }    
70     return 0;
71 } 

 

posted @ 2017-09-23 13:23  Yeader  阅读(226)  评论(0编辑  收藏  举报