HDU5418,Victor and World,货郎担(旅行商)问题的状态压缩DP解法,谈谈个人的理解和做法

原题:http://acm.hdu.edu.cn/showproblem.php?pid=5418

题意:T个测试样例,有n个点(1~n),m条距离不一定相同的线将他们链接起来,求从1出发将每个点都至少访问一次再回到1的最短距离。

这道题是货郎担的模板题,在n很小的时候,可以使用状态压缩dp来解决。

假如不太了解状态压缩dp的可以百度一下或者我之前写过博客讲状态压缩dp的:https://blog.csdn.net/weixin_43191865/article/details/88235731

我们用v表示点,s表示状态,s的二进制码中1表示没有去过的点,0表示去过的点

dp[v][s]:到达点v并且状态为s所需要的最短距离,那么最终状态则是 dp[1][0]

转移方程:(m数组存距离)

dp[v][s]=min(dp[v][s],dp[j][s-{j}]+m[i][j])

写成代码就是:(为什么j要+1,因为我的城市是从1开始的,可以结合后面代码来看)

dp[i][statu]=min(dp[i][statu],dp[j+1][statu|(1<

那么我在做这道题的时候有几个问题,我们不妨先来看一下代码,代码之后我会给出我自己的思考

#include
using namespace std;
const int maxn=20;
const int INF=0x3f3f3f3f;
int m[maxn][maxn],dp[maxn][1<>T;
	while(T--){
		scanf("%d %d",&n,&q); 
		memset(m,INF,sizeof(m)); //Fload 需要这个,不然会错 
		for(int i=1;i<=q;i++){
			int fro,to; 
			scanf("%d %d",&fro,&to);
			int why;
			scanf("%d",&why);
			m[fro][to]=min(m[fro][to],why); //漏了这个WA了N次,为什么要加这个 ? 
			m[to][fro]=m[fro][to];
		}
		for(int i=1;i<=n;i++) m[i][i]=0; //漏了这句 
		Flody(); //为什么要加弗洛伊德算法
		memset(dp,INF,sizeof(dp));
		dp[1][(1<=0;statu--){ //遍历状态不会导致有些状态无法转移吗? 
			for(int i=1;i<=n;i++){
				for(int j=0;j>j & 1)){ 
						dp[i][statu]=min(dp[i][statu],dp[j+1][statu|(1<

1.为什么在存边的时候需要加min 函数

一开始我没注意到这个点,题目的数据 n<=16,边最多也就16*16,但是题目给m的范围是m<100000,说明可能有重复给的边

2.为什么要弗洛伊德算法

题目没有说明每个点只能访问一遍,假如题目说明每个点只能访问一次,那么就不可以用(我认为)

3.使用for遍历状态的时候,不会导致有状态没有发生转移吗

比如A=min(A,B+M[A][B]),会不会因为B没有被先处理过(B=INF)而导致A没有成功转移呢

首先状态的遍历顺序是从大到小,我们使用1表示没去过,0表示去过。

状态转移间 的两个状态除了要转移的那个位,其他位都保持一样。

而状态转移的那个位是从1转移到0(0由1转移而来)

结合遍历顺序(从大到小),不会出现没有成功转移的情况

4.为什么那个dp转移方程不可以

两个dp方程之间

前者是:后面状态从前面状态转移过来

后者是:前面状态从后面状态转移过来

结合遍历顺序可知,后者行不通(可能说的有点蒙,但是结合状态statu遍历顺序可以想一想)

你可能感兴趣的:(acm)