poj2288 Islands and Bridges(状态压缩)

给出一个图,每个结点都有一个正权值,某些点之间有边相连,找总值最大的哈密顿回路,输出最大总值,和路径数量,

如果不存在该回路,输出'0 0'

总值计算方式:对于路径c1c2...cn,总值 由三部分组成:

Part1  -- v1+v2+....+vn

Part2 -- v[1]*v[2]+v[2]*v[3] +...v[n-1]*v[n]

Part3 -- 判断如果路径中ci 和 ci+2 之间右边,则加上 v[i]*v[i+1]*v[i+2]

解:状态:dp[s][i][j].cost 表示到达状态s,以i,j作为路径最末尾的 两个结点的总值,dp[s][i][j].cnt 表示路径种类数量。

初始状态:如果i和j之间有边相连,则dp[1<<i+1<<j][i][j] = v[i]+v[j]+v[i]*v[j];

转移:如果k和j之间有边相连,int tmp = dp[s][i][j]+v[k]+v[j]*v[k]  (if i和k有边相连) + v[i]*v[j]*v[k];

dp[s][j][k].cost = max(dp[s][j][k].cost,tmp);

dp[s][j][k].cnt : 当dp[s][j][k].cost == tmp 时,dp[s][j][k].cnt += dp[s][i][j].cnt;

若dp[s][j][k].cost < tmp 时,dp[s][j][k].cnt = dp[s][i][j].cnt;

最后我们要的结果dp[1<<(n-1)][i][j],二重循环遍历得到我们要的 总值最大的路径和对应的路径数量,

路径数量除以2再输出,因为题目要求 逆序的路径视为相同路径。

#include<stdio.h>
#include<memory.h>
const int maxn = 13+1;
typedef long long ll;
struct Node
{
	ll cost; //路径的总值 
	ll cnt;  //路径的数量 
};
int n,m;
ll val[maxn];
int cnct[maxn][maxn];
Node dp[1<<(maxn-1)][maxn][maxn];//到达状态i,路径倒数第2个结点是j,最后一个节点是k 
void init()
{
	memset(cnct,0,sizeof(cnct));
	for(int i=1;i<(1<<n);i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				dp[i][j][k].cnt = dp[i][j][k].cost = 0ll;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		init();
		
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&val[i]);
		}
		for(int i=1;i<=m;i++)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			cnct[a][b] = cnct[b][a] = 1;
		}
		if(n==1) 
		{
			printf("%d 1\n",val[1]);
			continue;
		}
		//边界,初始化已知情况的初值 
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(i==j)continue;
				if(cnct[i][j]==1)
				{
					dp[(1<<(i-1))+(1<<(j-1))][i][j].cnt = 1;
					dp[(1<<(i-1))+(1<<(j-1))][i][j].cost = val[i]+val[j]+val[i]*val[j];
				}
			}
		}
		//dp
		for(int s=1;s<(1<<n);s++)//每一个状态 
		{
			for(int i=1;i<=n;i++)
			{
				if((s&(1<<(i-1)))==0)continue;
				for(int j=1;j<=n;j++)
				{
					if(j==i || (s&(1<<(j-1)))==0) continue;
					if(cnct[i][j]==0) continue;
					if(dp[s][i][j].cnt == 0) continue;
					
					for(int k=1;k<=n;k++) 
					{
						if(k==i || k==j || (s&(1<<(k-1)))) continue;
						if(cnct[j][k]==0) continue;
						
						int ss = s|(1<<(k-1));
						ll ccnt = dp[s][i][j].cnt;
						ll ccost = dp[s][i][j].cost + val[k] + val[j]*val[k];
						if(cnct[i][k]==1) ccost += val[i]*val[j]*val[k];
						
						if(dp[ss][j][k].cnt == 0)
						{
							dp[ss][j][k].cnt = ccnt;
							dp[ss][j][k].cost = ccost;
						}else{
							if(dp[ss][j][k].cost < ccost)
							{
								dp[ss][j][k].cnt = ccnt;
								dp[ss][j][k].cost = ccost;
							}else if(dp[ss][j][k].cost == ccost)
							{
								dp[ss][j][k].cnt += ccnt;
							}
						}
					}
				}
			}	
		} 
		//找状态为 (1<<n)-1 中总值最大的 路径,及情况数 
		ll ans = 0;
		ll sum = 0;
		int end = (1<<n)-1;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(ans < dp[end][i][j].cost)
				{
					ans = dp[end][i][j].cost;
					sum = dp[end][i][j].cnt;	
				}else if(ans == dp[end][i][j].cost)
				{
					sum += dp[end][i][j].cnt;
				}
			}
		}
		if(ans == 0) printf("0 0\n");
		else printf("%lld %lld\n",ans,sum/(2ll));//不重复,情况数除以2 
	}
	return 0;
}

渣科还是继续努力吧.....

猜你喜欢

转载自blog.csdn.net/zark721/article/details/81083739
今日推荐