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<<j)]+m[i][j+1]);

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

#include<bits/stdc++.h>
using namespace std;
const int maxn=20;
const int INF=0x3f3f3f3f;
int m[maxn][maxn],dp[maxn][1<<maxn];
int n,q;
void Flody(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++) m[i][j]=min(m[i][j],m[i][k]+m[k][j]);
		}
	}
	return ;
}
int main(){
	int T; cin>>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<<n)-1]=0;
		//下面首先遍历状态,然后遍历点 
		for(int statu=(1<<n)-1;statu>=0;statu--){ //遍历状态不会导致有些状态无法转移吗? 
			for(int i=1;i<=n;i++){
				for(int j=0;j<n;j++){
					if(!(statu>>j & 1)){ 
						dp[i][statu]=min(dp[i][statu],dp[j+1][statu|(1<<j)]+m[i][j+1]);
//						dp[j][statu^(1<<j)]=min(dp[j][statu^(1<<j)],dp[i][statu]+m[i][j]); 为什么不能用这个状态转移? 
					}
				}
			}
		}
		cout<<dp[1][0]<<endl;
	}
	return 0;
}
/*
1
3 2
1 2 2
1 3 3
*/

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遍历顺序可以想一想)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值