PKU 2288 Islands and Bridges 状态dp

题意:

  给你一张地图,上面有一些岛和桥。你要求出最大的三角哈密顿路径,以及他们的数量。

  哈密顿路:一条经过所有岛的路径,每个岛只经过一次。

  最大三角哈密顿路:满足价值最大的哈密顿路。

    价值计算分为以下三部分:

      1. 所有点权的和。

      2. 对于路径上任意两个连续的点(共享一条边)的点权乘积的和。

      3. 对于路径上任意三个连续的点,如果他们构成一个三角形(两两之间有边),那么加上三点点权的乘积

思路:

  状态压缩动态规划, dp[st][i][j] 表示状态是st, 前一步在i,现在停在j的最大价值。

    cnt[st][i][j] 表示计数。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 using namespace std;
 6 
 7 typedef __int64 ll;
 8 
 9 const int MAXN= 13;
10 
11 ll dp[1<<13][MAXN][MAXN];
12 ll cnt[1<<13][MAXN][MAXN];
13 
14 int G[20][20];
15 ll val[20];
16 int n, m;
17 
18 void print() {
19     for (int i = 0; i <= n; i++) {
20         for (int j = 0; j <= n; j++)
21             printf("%d ", G[i][j]);
22         puts("");
23     }
24 }
25 
26 int main() {
27     #ifdef Phantom01
28         freopen("PKU2288.txt", "r", stdin);
29     #endif // Phantom01
30 
31     int T;
32     scanf("%d", &T);
33     while (T--) {
34         scanf("%d%d", &n, &m);
35         memset(dp, 0, sizeof(dp));
36         memset(cnt, 0, sizeof(cnt));
37         memset(G, 0, sizeof(G));
38         for (int i = 0; i < n; i++) {
39             scanf("%I64d", &val[i]);
40         }
41         for (int i = 0; i < m; i++) {
42             int u, v;
43             scanf("%d%d", &u, &v);
44             G[u-1][v-1] += 1;
45             G[v-1][u-1] += 1;
46         }
47 //print();
48         if (1==n) {
49             printf("%I64d 1\n", val[0]);
50             continue;
51         }
52 
53         for (int i = 0; i < n; i++)
54             for (int j = 0; j < n; j++) if (G[i][j]){
55                 dp[(1<<i)|(1<<j)][i][j] = val[i] + val[j] + val[i]*val[j];
56                 cnt[(1<<i)|(1<<j)][i][j] += G[i][j];
57             }
58 
59         for (int i = 1; i < (1<<n)-1; i++)
60             for (int j = 0; j < n; j++) if (i&(1<<j))
61                 for (int u = 0; u < n; u++) if ((i&(1<<u)) && (j!=u) && cnt[i][j][u])
62                     for (int v = 0; v < n; v++) if (G[u][v] && !(i&(1<<v))) {
63                         ll &now = dp[i][j][u];
64                         ll &next = dp[i|(1<<v)][u][v];
65                         ll va = val[v]*(1 + val[u]);
66                         if (G[j][v]) va += val[j]*val[u]*val[v];
67                         if (next < now+va) {
68                             next = now+va;
69                             cnt[i|(1<<v)][u][v] = cnt[i][j][u];
70                         } else if (next==now+va)
71                             cnt[i|(1<<v)][u][v] += cnt[i][j][u];
72                     }
73 
74         ll ans = 0, c = 0;
75         for (int i = 0; i < n; i++)
76             for (int j = 0; j < n; j++)
77                 if (ans<dp[(1<<n)-1][i][j]) {
78                     ans = dp[(1<<n)-1][i][j];
79                     c = cnt[(1<<n)-1][i][j];
80                 } else if (ans==dp[(1<<n)-1][i][j])
81                     c += cnt[(1<<n)-1][i][j];
82 
83         printf("%I64d %I64d\n", ans, c/2);
84     }
85 
86     return 0;
87 }
PKU2288

P.s.: 开始多开了一维导致MLE,后来发现读错题了 0 0 结果花了一晚上

posted @ 2014-04-25 20:26  Phantom01  阅读(212)  评论(0编辑  收藏  举报