hdu 3001 Travelling (三进制)状压dp
Posted 00isok
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu 3001 Travelling (三进制)状压dp相关的知识,希望对你有一定的参考价值。
<题目链接>
题目大意:
给出n个点和m条边,求经过所有点所需的最小花费,每个点最多经过两次。
解题分析:
TSP问题类型,由于此题每个点有三种状态,所以采用三进制状态压缩,0、1、2 分别代表经过这个点的次数,然后就与TSP的dp解法类似,dp[i][j]代表状态为i,以 j 城市作为旅途的最后一个点所需的最小花费 。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> using namespace std; int dp[60000][11];///dp[i][j]表示i状态下以j结尾的最短步数 int three[11];///three[i]表示3的i次方是多少 int digit[60000][11];///digit[i][j]表示状态i的第j位是什么数字(0,1,2) int edge[15][15]; //记录两点之间路径的值 int n,m; const int INF=0x3f3f3f3f; void init() { three[0]=1; for(int i=1; i<11; i++) three[i]=three[i-1]*3; for(int i=0; i<three[10]; i++) { int tem=i; //tem代表当前的状态序号 for(int j=0; j<10; j++) { digit[i][j]=tem%3; //将状态压缩的当前状态的每一位的实际情况计算出来 tem/=3; } } } int main() { init(); //将每一状态的所有点对应实际情况预先打表处理 while(~scanf("%d%d",&n,&m)) { for(int i=0; i<n; i++) for(int j=0; j<n; j++) edge[i][j]=INF; //两点距离初始化,为后面的去重做准备 for(int i=0;i<three[n];i++) for(int j=0; j<n; j++) dp[i][j]=INF; //因为最后要求的是最短距离,所以初始化为INF while(int i=0;i<m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); edge[a-1][b-1]=edge[b-1][a-1]=min(c,edge[a-1][b-1]); //去重 } for(int i=0;i<n; i++) dp[three[i]][i]=0; //dp的初始化,将起点都找出来 ,所有点的标号为0~n-1 int ans=INF; for(int j=0; j<three[n]; j++) //枚举所有状态 { bool flag=1; for(int i=0;i<n; i++) { if(digit[j][i]==0)flag=0;///只要三进制数中存在一个0,那么就说明还有点没有遍历完,就不能当做最终答案来求,即筛选掉那些不符合题意的状态 if(dp[j][i]!=INF) for(int k=0;k<n;k++) if(edge[i][k]!=INF&&digit[j][k]!=2)///注意这个digit[j][k]!=2,因为如果j状态在k点已经走过两次了显然是不能继续往下走的 dp[j+three[k]][k]=min(dp[j][i]+edge[i][k],dp[j+three[k]][k]); //更新加入edge[i][k]边的最小值,j+three[k]表示用当前状态j,加入edge[i][k]后,更新状态j+three[k]的值 } if(flag) for(int i=0; i<n; i++)///由于是3进制,不能方便的判断一串三进制数里面是否存在0,所以在dp过程中一边dp一边直接统计结果,也是迫于无奈T_T ans=min(ans,dp[j][i]); } if(ans>=INF) printf("-1 "); else cout<<ans<<endl; } return 0; }
2018-09-07
以上是关于hdu 3001 Travelling (三进制)状压dp的主要内容,如果未能解决你的问题,请参考以下文章
hdu 3001 Travelling 经过所有点(最多两次)的最短路径 三进制状压dp