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。

View Code
 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 typedef __int64 i64;
 5 
 6 const int maxn = 13;
 7 const int maxs = 1<<maxn | 1;
 8 
 9 i64 dp[maxn][maxn][maxs], way[maxn][maxn][maxs];
10 int map[maxn][maxn], v[maxn];
11 int n, m, S;
12 
13 void StateCompressDp() {
14 
15     memset(dp, -1, sizeof(dp));
16     memset(way, 0, sizeof(way));
17     for (int i = 0; i < n; i++) {
18         for (int j = 0; j < n; j++) {
19             if (map[i][j]) {
20                 dp[i][j][(1<<i)+(1<<j)] = v[i] + v[j] + v[i]*v[j];
21                 way[i][j][(1<<i)+(1<<j)] = 1;
22             }
23 
24         }
25     }
26     for (int p = 3; p < S; p++) {
27         for (int i = 0; i < n; i++) {
28             if (!(p&1<<i)) continue;
29             for (int j = 0; j < n; j++) {
30                 if (i == j || !(p&1<<j) || dp[i][j][p] == -1) continue;
31                 for (int k = 0; k < n; k++) {
32                     if (p&1<<k || !map[j][k]) continue;
33                     int r = p + (1 << k);
34                     i64 q = dp[i][j][p] + v[k] + v[j]*v[k];
35                     if (map[i][k]) {
36                         q += v[i] * v[j] * v[k];
37                     }
38                     if (q > dp[j][k][r]) {
39                         dp[j][k][r] = q;
40                         way[j][k][r] = way[i][j][p];
41                     } else if (q == dp[j][k][r]) {
42                         way[j][k][r] += way[i][j][p];
43                     }
44                 }
45             }
46         }
47     }
48 
49     return ;
50 }
51 
52 int main () {
53 
54     int t;
55 
56     scanf("%d", &t);
57     while (t--) {
58         int tmpx, tmpy;
59         memset(map, 0, sizeof(map));
60         scanf("%d%d", &n, &m);
61         for (int i = 0; i < n; i++) {
62             scanf("%d", &v[i]);
63         }
64         for (int i = 0; i < m; i++) {
65             scanf("%d%d", &tmpx, &tmpy);
66             map[tmpx-1][tmpy-1] = map[tmpy-1][tmpx-1] = 1;
67         }
68         S = 1 << n;
69         if (n == 1) {
70             printf("%d %d\n", v[0], 1);
71         } else {
72             StateCompressDp();
73             i64 ansv = -1, ansp = 0;
74             for (int i = 0; i < n; i++) {
75                 for (int j = 0; j < n; j++) {
76                     if (i == j) continue;
77                     if (dp[i][j][S-1] > ansv) {
78                         ansv = dp[i][j][S-1];
79                         ansp = way[i][j][S-1];
80                     } else if (dp[i][j][S-1] == ansv) {
81                         ansp += way[i][j][S-1];
82                     }
83                 }
84             }
85             printf("%I64d %I64d\n", ansv == -1 ? 0 : ansv, ansp/2);
86         }
87     }
88 
89     return 0;
90 }

 

转载于:https://www.cnblogs.com/chasetheexcellence/archive/2012/04/17/poj2288.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值