标签:
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/131072 K (Java/Others)
Total Submission(s): 958 Accepted Submission(s): 431
题意: 给一个地图,从其中一个点开始走,遍历完所有的点后最后再回到这个点,求最短路径
题解: 注意数据范围给的是16个点,又是求汉密顿环路问题,就想到了状态压缩dp,这个题可以学习一个很好地思想就是spfa的思想,将其用到dp中,取出队首的状态,看这个状态停在哪个点,用这个点更新所有的可以更新的状态,因为每一个点可以多次的遍历,所以每次更新都要从0到n依次遍历,新更新的状态如果之前没有访问过,则说明这个状态有更新其他状态的潜力,所以将其压入队列中,通过这种方式可以更新所有的状态。
简单说一下状态压缩dp,用一个二进制的数表示某个点是否在集合内,全集为(1<<n)-1 ; j集合中加入一个元素k是 j|(1<<k) ; 在j集合中去掉一个元素k是 j^(1<<k) ;
dp[j][k] 表示走过集合j中的所有元素最后停留在k点的最短路径
转移方程: dp[s|(1<<i)][i] = min(dp[s|(1<<i)][i] , dp[s][u]+mp[u][i])
注意事项: 这里的边是双向边,在输入边的时候会有很多的无效边,要取最小的。
用qair<int , int> 相当于一个struct{ int a, int b};
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<algorithm> 5 using namespace std; 6 7 int dp[1<<17][17]; 8 int mp[17][17]; 9 int dis[17]; 10 bool vis[1<<17][17]; 11 int n; 12 13 int main() 14 { 15 int T , m ; 16 scanf("%d",&T); 17 while(T--) 18 { 19 scanf("%d%d",&n,&m); 20 memset(mp,0x7f,sizeof(mp)); 21 for(int i =0 ; i < n ;i++) mp[i][i] = 0; 22 for(int i =0 ; i < m ;i++) { 23 int u , v , d; 24 scanf("%d%d%d",&u,&v,&d); 25 u--,v--; 26 mp[u][v] = mp[v][u] = min(mp[u][v],d); 27 } 28 memset(dp,0x7f,sizeof(dp)); 29 memset(vis,0,sizeof(vis)); 30 dp[0][0] = 0 ; vis[0][0] = 1; 31 queue<pair<int,int> > q; 32 q.push(make_pair(0,0)); 33 while(!q.empty()) 34 { 35 int s = q.front().first; 36 int u = q.front().second; 37 q.pop(); 38 for(int i = 0 ; i < n ; i++) 39 { 40 int ss = s|(1<<i); 41 if(dp[ss][i]>dp[s][u] + mp[u][i]){ 42 dp[ss][i] = dp[s][u] + mp[u][i]; 43 if(vis[ss][i] == 0){ 44 vis[ss][i] = 1 ; 45 q.push(make_pair(ss,i)); 46 } 47 } 48 } 49 } 50 printf("%d\n",dp[(1<<n)-1][0]); 51 } 52 return 0 ; 53 }
标签:
原文地址:http://www.cnblogs.com/shanyr/p/4827563.html