poj 3311 和饼一起吃 题解

版权声明:八月炊火的博客如需转载请注明出处 https://blog.csdn.net/qq_34990731/article/details/83378341

题目链接
知识点: floyd、状压DP
讲解:
对于这一题其实就是旅行商问题。我们很容易就想到首先我们要处理出每两个点之间的最短路,这就是floyd,处理完后我们就可以开始DP了,因为最多有10个送货点所以我们状压每个送货点是否送到,又因为我们最后是要回来的所以我们在定义状态时不可以单单只有每个点的状态,还要有现在到了哪个点,这样我们最后答案就是现在到了0这个点(回来了),并且全部送货地点都去过了,所以我们定义dp[i][j]表示现在到了i这个点,送货状态为j。状态转移就是dp[j][i]=min(dp[k][i^(1<<j)]+f[k][j],dp[j][i]);j和k都是i集合中的点,而且j!=k,这个就相当于在集合i中我们找一个点j,我们可以从i集合少掉j这个点的集合转移过来,然后我们再枚举i集合中不与j相同的点k表示从k走到j,这样这一题就搞定了。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int f[20][20],dp[20][1<<12];
int main()
{
	int n;
	while(1)
	{
		scanf("%d",&n);
		if(n==0)//多组数据以0结尾 
			break;
		memset(dp,0x3f,sizeof(dp));
		memset(f,0x3f,sizeof(f));
		for(int i=0;i<=n;i++)
			for(int j=0;j<=n;j++)
				scanf("%d",&f[i][j]);//在读入数据的时候我们可以直接当做给floyd赋初值 
		for(int i=0;i<=n;i++)
			for(int j=0;j<=n;j++)
			{
				if(i==j)
					continue;
				for(int k=0;k<=n;k++)
				{
					if(k==i || k==j)
						continue;
					f[i][j]=min(f[i][j],f[i][k]+f[k][j]);//跑一边floyd 
				}
			}
		for(int i=0;i<=n;i++)
			dp[i][1<<i]=f[0][i];//我们设置边界表示我们从0号点走到其他点 
		for(int i=0;i<=(1<<(n+1))-1;i++)//枚举每个集合 
		{
			for(int j=0;j<=n;j++)
			{
				if(i&(1<<j)==0)//不在集合就跳过 
					continue;
				for(int k=0;k<=n;k++)
				{
					if(i&(1<<k)==0 || j==k)//j==k就跳过 
						continue;
					dp[j][i]=min(dp[k][i^(1<<j)]+f[k][j],dp[j][i]);//我们去更新一下我们的值 
				}
			}
		}
		printf("%d\n",dp[0][(1<<(n+1))-1]);//输出答案 
	}
	return 0;
}

希望大家都可以AC。
PS:
有疑问的欢迎留言。

猜你喜欢

转载自blog.csdn.net/qq_34990731/article/details/83378341