首页 > 代码库 > poj 2288 Islands and Bridges_状态压缩dp_哈密尔顿回路问题

poj 2288 Islands and Bridges_状态压缩dp_哈密尔顿回路问题

题目链接

题目描写叙述:哈密尔顿路问题。n个点,每个点有权值,设哈密尔顿路为 C1C2...Cn,Ci的权值为Vi,一条哈密尔顿路的值分为三部分计算:
1.每个点的权值之和
2.对于图中的每一条CiCi+1,加上Vi*Vi+1
3.对于路径中的连续三个点:CiCi+1Ci+2,若在图中,三点构成三角形,则要加上Vi*Vi+1*Vi+2
求一条汉密尔顿路能够获得的最大值,而且还要输出有多少条这种哈密尔顿路。

这道题的状态感觉不是非常难想,由于依据一般的哈密尔顿路问题,首先想到的是设计二维状态,dp[i , s]表示当前在i点,走过的点形成状态集合s。可是这道题在求解值的时候有一个不一样的地方,就是第三部分,假设还是设计成二维的状态,就会非常麻烦,由于每添加一个新点,要推断新点、当前点、倒数第二个点是否构成三角形,所以要记录倒数第二个点。非常自然地想到扩展状态的维数,添加一维,记录倒数第二个点。

1>  设计状态:
dp[i , j , s]表示当前站在j点,前一个点是i点,形成的状态集合是s,此时的最大值,way[i , j , s]记录当前状态下达到最大值的路径数;
2>  状态转移:
设k点不在集合s中,且存在边<j , k>
设q为下步到达k点获得的最大值
令r = s + (1<<k),为当前站在点k,前一个点为j,形成状态集合r
若i,j,k形成三角形,则q = dp[i][j][s] + v[k] + v[j]*v[k] + v[i]*v[j]*v[k]
否则,q = dp[i][j][s] + v[k] + v[j]*v[k];
若q大于dp[j][k][r];则:
dp[j][k][r] = q
way[j][k][r] = way[i][j][s];
若q等于dp[j][k][r],则:
way[j][k][r] += way[i][j][s];
3>  初始化:
显然,若i点到j点有边,则: 
dp[i][j][(1<<i)+(1<<j)] = v[i] + v[j] + v[i]*v[j];
way[i][j][(1<<i)+(1<<j)] = 1;
4>  结果的产生:
最后的结果我们要枚举点i和j,找到最大的dp[i][j][(1<<n)-1],而且更新记录路径数ansp,最后ansp要除2才是结果,由于题目最后一句话,正向反向是一样的路。
此外,须要注意的是discuss提到的特殊情况,要用__int64,而且注意n等于1时,最大值就是第一个点的权值,路径数为1。


#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN =13;
const int MAXS =1<<MAXN|1;
#define ll __int64 
ll dp[MAXN][MAXN][MAXS],way[MAXN][MAXN][MAXS];
int map[MAXN][MAXN],v[MAXN];
int n,m,s;
void stateDp(){
	int i,j,p,k;
	memset(dp,-1,sizeof(dp));
	memset(way,0,sizeof(way));
	for(i=0;i<n;i++)
		for(j=0;j<n;j++){
			if(map[i][j]){
				dp[i][j][(1<<i)+(1<<j)]=v[i]+v[j]+v[i]*v[j];
				way[i][j][(1<<i)+(1<<j)]=1;
			}
		}
	for(p=3;p<s;p++){
		for(i=0;i<n;i++){
			if(!(p&1<<i))//假设该状态第i城市没有路过就跳过 
				continue;
			for(j=0;j<n;j++){
				if(i==j||!(p&1<<j)||dp[i][j][p]==-1)
					continue;
				for(k=0;k<n;k++){
					if(p&1<<k||!map[j][k])//假设k存在该状态则跳过 
						continue;
					int r=p+(1<<k);//状态增加k城市
					ll q=dp[i][j][p]+v[k]+v[j]*v[k];//更新价值 
					if(map[i][k]){//当构成环时更新价值 
						q+=v[i]*v[j]*v[k];
					} 
					if(q>dp[j][k][r]){
						dp[j][k][r]=q;
						way[j][k][r]=way[i][j][p];
					}else if(q==dp[j][k][r]){//相等时,有多个相等价值路径 
						way[j][k][r]+=way[i][j][p];
					}
				}
			} 
		}
	}
}
int main(int argc, char** argv) {
	
	int t,x,y,i,j;
	scanf("%d",&t);
	while(t--){
		memset(map,0,sizeof(map));
		scanf("%d%d",&n,&m);
		for(i=0;i<n;i++)
			scanf("%d",&v[i]);
		for(i=0;i<m;i++){
			scanf("%d%d",&x,&y);
			map[x-1][y-1]=map[y-1][x-1]=1;
		}
		s=1<<n;
		if(n==1){
			printf("%d %d\n",v[0],1);
			continue;
		}
		stateDp();
		ll ansv=-1,ansp=0;
		for(i=0;i<n;i++)
			for(j=0;j<n;j++){
				if(i==j)continue;
				if(dp[i][j][s-1]>ansv){//s-1为经过全部岛 
					ansv=dp[i][j][s-1];
					ansp=way[i][j][s-1];
				}else if(dp[i][j][s-1]==ansv){
					ansp+=way[i][j][s-1];
				}
			}
		printf("%I64d %I64d\n",ansv==-1?0:ansv,ansp/2);
	}
	return 0;
}