HDU3001 travelling 状压DP

题目

题意:旅行商问题,特点是一个点最多经过两次。

题解:和只能经过一次的旅行商一样使用状态压缩,但是不用二进制而是用三进制表示。

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int f3[11];
void pow3()//3的次幂打表
{
	int i;
	f3[0]=1;
	for(i=1;i<11;i++)f3[i]=3*f3[i-1];
}
int dp[11][60000],mp[11][11],minn,bit3[11],n;//dp[i][j]记录三进制数j以i结尾时的最小花费
int turnp(int bit3[],int state)//解压缩,求状态下经历的点数
{
	int i;
	int cnt=0;
	for(i=0;i<n;i++)
	{
		bit3[i]=state%3;
		state/=3;
		if(bit3[i])cnt++;
	}
	return cnt;
}
void init()//初始化
{
	int i,j;
	for(i=0;i<n;i++){
		for(j=0;j<n;j++)mp[i][j]=-1;
		for(j=0;j<f3[n];j++)dp[i][j]=-1;
	}
}
int main()
{
	int m,a,b,c,i,k,j;
	pow3();
	while(~scanf("%d%d",&n,&m)){
		init();
		for(i=0;i<m;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			a--,b--;
			if(mp[a][b]==-1)mp[a][b]=mp[b][a]=c;
			else mp[a][b]=mp[b][a]=min(mp[b][a],c);
		}
		minn=-1;
		int now;
		for(now=0;now<f3[n];now++)//先从状态遍历
		{
			k=turnp(bit3,now);
			for(i=0;i<n;i++)
			{
				if(bit3[i])
				{
					if(k==1)dp[i][now]=0;//只经历了一个点那就时以此为起点的刚开始的状态
					if(dp[i][now]==-1)continue;//在之前没被遍历到,说明i在这种状态下不能作为结尾
					if(k==n)//遍历所有点
					{
						if(minn==-1)minn=dp[i][now];
						else minn=min(minn,dp[i][now]);
					}
					for(j=0;j<n;j++)//状态转移部分
					{
						if(j!=i&&mp[i][j]!=-1&&bit3[j]<2)
						{
							int next=now+f3[j];
							if(dp[j][next]==-1)dp[j][next]=dp[i][now]+mp[i][j];
							else dp[j][next]=min(dp[j][next],dp[i][now]+mp[i][j]);
						}
					}
				}
			}
		}
		printf("%d\n",minn);
	}
    return 0;
}


猜你喜欢

转载自blog.csdn.net/nwpu2017300135/article/details/80428189