POJ-2288 Islands and Bridges 状态压缩DP TSP

题目链接

题目描述:哈密尔顿路问题。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。

转至http://www.cnblogs.com/chasetheexcellence/archive/2012/04/17/poj2288.html


#include <stdio.h>
#include <string.h>
const int maxn = 13;
typedef __int64 LL;
int n,m,S;
bool map[maxn][maxn];
int v[maxn];
LL dp[maxn][maxn][1<<13],way[maxn][maxn][1<<13];  //dp[i][j][k]前个点在i,当前点在j,状态为k
void GetDp()
{
	memset( dp,-1,sizeof(dp) );
	memset( way,0,sizeof(way) );
	for( int i = 0; i < n; i ++ ){           //初始化2个点的状态
		for( int 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( int p = 3; p < S; p ++ ){        //状态
		for( int i = 0; i < n; i ++ ){  //前个点
			if( !(p&(1<<i)) )	continue;
			for( int j = 0; j < n; j ++ ){  //当前点
				if( i == j || !(p&(1<<j)) || dp[i][j][p] == -1 )	continue;
				for( int k = 0; k < n; k ++ ){
					if( p&(1<<k) || !map[j][k] )	continue;  //k已走过或j,k不通
					int r = p + (1<<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()
{
	#ifndef ONLINE_JUDGE
    freopen("data.txt","r",stdin);
	#endif
	int cas,a,b;
	scanf("%d",&cas);
	while( cas -- ){
		scanf("%d%d",&n,&m);
		memset( map,0,sizeof(map) );
		for( int i = 0; i < n; i ++ )
			scanf("%d",&v[i]);
		for( int i = 0; i < m; i ++ ){
			scanf("%d%d",&a,&b);
			map[a-1][b-1] = map[b-1][a-1] = 1;
		}
		if( n == 1 ){
			printf("%d %d\n",v[0],1);
			continue;
		}
		S = 1<<n;
		GetDp();
		LL ansv = -1,ansp = 0;
		//在起点i终点j状态为S-1中选出最大的值与总路径数
		for( int i = 0; i < n; i ++ ){                       
			for( int j = 0; j < n; j ++ ){
				if( i == j ) continue;
				if( dp[i][j][S-1] > ansv ){
					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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值