poj 2288 Islands and Bridges解题报告-状态压缩dp

题目链接:http://poj.org/problem?id=2288

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

(ps:在处理特殊情况时,忘记换行,还PE一次,这年头PE的还真少见啊。。。)

代码如下,祝1Y。

#include <stdio.h>
#include <string.h>

typedef __int64 i64;

const int maxn = 13;
const int maxs = 1<<maxn | 1;

i64 dp[maxn][maxn][maxs], way[maxn][maxn][maxs];
int map[maxn][maxn], v[maxn];
int n, m, S;

void StateCompressDp() {

	memset(dp, -1, sizeof(dp));
	memset(way, 0, sizeof(way));
	for (int i = 0; i < n; i++) {
		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;
					int r = p + (1 << k);
					i64 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];
					}
				}
			}
		}
	}

	return ;
}

int main () {

	int t;

	scanf("%d", &t);
	while (t--) {
		int tmpx, tmpy;
		memset(map, 0, sizeof(map));
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; i++) {
			scanf("%d", &v[i]);
		}
		for (int i = 0; i < m; i++) {
			scanf("%d%d", &tmpx, &tmpy);
			map[tmpx-1][tmpy-1] = map[tmpy-1][tmpx-1] = 1;
		}
		S = 1 << n;
		if (n == 1) {
			printf("%d %d\n", v[0], 1);
		} else {
			StateCompressDp();
			i64 ansv = -1, ansp = 0;
			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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值