[POJ2288] [2004上海区域赛] Islands and Bridges [状态压缩][dp]

本文探讨了一种解决特定图论问题的方法,即寻找无向图中权值最大的哈密顿回路及其数量。通过状态压缩动态规划(TSP变形),解决了包含1至13个点及最多100条边的图的问题,详细阐述了算法实现过程。

[ L i n k \frak{Link} Link]
给定一个 1 ≤ n ≤ 13 \frak{1\le n\le13} 1n13 1 ≤ m ≤ 100 \frak{1\le m\le100} 1m100边无向图,每个点有权值 1 ≤ v i ≤ 100 \frak{1\le v_i\le 100} 1vi100
求权值最大的哈密顿回路(以某个点为起点,经过其他每个点一次的回路)的权值和数量。权值定义如下:
 1.记 V 1 \frak{V_1} V1表示 ∑ v i \frak{\sum v_i} vi
 2.记 V 2 \frak{V_2} V2表示 ∑ v i v i + 1 \frak{\sum v_iv_{i+1}} vivi+1 v i \frak{v_i} vi v i + 1 \frak{v_{i+1}} vi+1在哈密顿回路中且被边直接相连
 3.记 V 3 \frak{V_3} V3表示 ∑ v i v i + 1 v i + 2 \frak{\sum v_iv_{i+1}v_{i+2}} vivi+1vi+2 v i \frak{v_{i}} vi v i + 1 \frak{v_{i+1}} vi+1 v i + 2 \frak{v_{i+2}} vi+2在哈密顿回路中且构成一个连通分量
 那么权值 V = ∑ i = 1 3 V i \frak{V=\sum\limits_{i=1}^3V_i} V=i=13Vi

看见题目的第一反应: ??????????????????


拆开来考虑每个权值。
在某一点时候的状态由之前的两个点决定。按照访问顺序将这三个点标为 A , B , C \frak{A,B,C} A,B,C
发现一定会有 V 1 \frak{V_1} V1 V 2 \frak{V_2} V2。而 V 3 \frak{V_3} V3题目也提示了,只要 A \frak{A} A C \frak{C} C有边直接相连就会产生。
这样一来发现其实转移并不难。这道题也确实就只是一个普通的TSP变形。状压即可。

本题细节很多,要注意。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
int n,m;
long long Ans=0,t;
long long BAns;
long long Pt[15]={};
bool lnk[15][15]={};
long long F[8200][15][15]={};
long long C[8200][15][15]={};
int main()
{
	scanf("%d",&n);
	while(~scanf("%d%d",&n,&m))
	{
		Ans=-1; BAns=0;
		memset(lnk,0,sizeof(lnk));
		memset(C,0,sizeof(C));
		memset(F,-1,sizeof(F));
		for(int i=1;i<=n;++i)
		{
			scanf("%lld",&Pt[i]);
		}
		if(n==1){printf("%lld 1\n",Pt[1]);continue;} //***
		for(int u,v,i=1;i<=m;++i)
		{
			scanf("%d%d",&u,&v);
			lnk[u][v]=lnk[v][u]=1;
		}
		for(int a=1;a<=n;++a)
		{
			for(int b=1;b<=n;++b)
			{
				if(a==b||lnk[a][b]==0)continue;
				F[(1<<a-1)|(1<<b-1)][a][b]=Pt[a]*Pt[b]+Pt[a]+Pt[b];
				C[(1<<a-1)|(1<<b-1)][a][b]=1; //*
			}
		}
		for(int i=1;i<(1<<n);++i)
		{
			for(int a=1;a<=n;++a)
			{
				if(!(i&(1<<a-1)))continue;
				for(int b=1;b<=n;++b)
				{
					if(!(i&(1<<b-1)))continue;
					if(lnk[a][b]==0||a==b)continue;
					if(F[i][a][b]==-1)continue;
					for(int v,t,c=1;c<=n;++c)
					{
						if(i&(1<<c-1))continue;
						if(lnk[b][c]==0||b==c||a==c)continue;
						v=i|(1<<c-1);
						t=F[i][a][b]+Pt[b]*Pt[c]+Pt[c]+((lnk[a][c])?(Pt[a]*Pt[b]*Pt[c]):0);
						if(t>F[v][b][c])F[v][b][c]=t,C[v][b][c]=C[i][a][b]; //*
						else if(t==F[v][b][c])C[v][b][c]+=C[i][a][b]; //*
					}
				}
			}
		}
		t=(1<<n)-1;
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				if(i==j)continue;
				if(F[t][i][j]>Ans)
				{
					Ans=F[t][i][j];
					BAns=C[t][i][j];
				}
				else if(F[t][i][j]==Ans)BAns+=C[t][i][j];
			}
		}
		if(Ans<0)printf("0 0\n");
		else printf("%lld %lld\n",Ans,BAns>>1);
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值