44 P10963 Islands and Bridges 题解

Island and Bridges

题面

给定一张有 \(n\) 个点, \(m\) 条边的无向图,每个点有权值 \(v_i\)

哈密顿路径为访问每个点恰好一次的路径

定义一个哈密顿路径的权值为以下三个价值的和

  • 每个岛的权值之和
  • 哈密顿路径中的每条边连接的相邻点的权值乘积之和
  • 哈密顿路径中能成环的连续三个点的权值乘积之和

任务1:找出哈密顿路径价值最大值

任务2:找到满足最大价值的路径有多少条

\(1 \le n \le 13, \ 1 \le m \le \frac {n(n - 1)} 2\)

题解

这道题的思路并不难想,但是这道题任务2我搞错了

由于 \(n\) 的数据范围很小,并且路径这个东西不好统计

所以我们维护一个集合表示已经走过的点,因为要计算第二种和第三种价值,所以我们还需要记这条路径最后的两个点

所以设 \(f(s,i,j)\) 表示走过的点的集合为 \(s\) ,倒数第二个点为 \(i\) ,最后一个点为 \(j\) 的最大价值

转移

\[f(s,j,k) = \max \{ f(s \oplus 2^{k - 1},i,j) + calc \} \]

还有个问题是路径条数怎么算?

我一开始的想法是直接看最后的答案和最大值相等的 \(f(2^n - 1, i, j)\) 有多少个,然后再除以2

但是错了,后来看题解才意识到 \(f(2^n - 1, i, j)\) 可能从多个状态转移

所以它代表的不是一种情况,而是很多种情况

所以我们再记 \(g(s,i,j)\) 表示走过的点的集合为 \(s\) ,倒数第二个点为 \(i\) ,最后一个点为 \(j\) ,最大价值的方案数,这样就能够正确统计方案数了

时间复杂度为 \(O(2^nn^3)\)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 15, M = (1 << 13) + 10;

int n, m;
int a[N][N], val[N];
int f[M][N][N];
ll g[M][N][N];

void solve () {
    //初始化
    memset (a, 0, sizeof a);

    //读入
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) {
        cin >> val[i];
    }
    for (int i = 1; i <= m; i ++) {
        int x, y;
        cin >> x >> y;
        a[x][y] = a[y][x] = 1;
    }
    if (n == 1) {
        cout << val[1] << ' ' << 1 << endl;
        return;
    }


    //初始化
    memset (f, -0x3f, sizeof f);
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= n; j ++) {
            if (i == j) continue;
            if (!a[i][j]) continue;
            int s = (1 << i - 1) | (1 << j - 1);
            f[s][i][j] = val[i] + val[j] + val[i] * val[j];
            g[s][i][j] = 1;
        }
    }

    //状压dp
    for (int s = 1; s < (1 << n); s ++) {
        for (int i = 1; i <= n; i ++) {
            if (!(s & (1 << i - 1))) continue;
            for (int j = 1; j <= n; j ++) {
                if (!(s & (1 << j - 1))) continue;
                for (int k = 1; k <= n; k ++) {
                    if (!(s & (1 << k - 1))) continue;
                    if (i == j || j == k || i == k) continue;
                    if (!a[i][j] || !a[j][k]) continue;

                    int v = val[k] + val[j] * val[k] + a[i][k] * val[i] * val[j] * val[k];
                    if (f[s][j][k] < f[s ^ (1 << k - 1)][i][j] + v) {
                        f[s][j][k] = f[s ^ (1 << k - 1)][i][j] + v;
                        g[s][j][k] = g[s ^ (1 << k - 1)][i][j];
                    } else if (f[s][j][k] == f[s ^ (1 << k - 1)][i][j] + v) {
                        g[s][j][k] += g[s ^ (1 << k - 1)][i][j];
                    }
                }
            }
        }
    }

    int ans = 0;
    ll cnt = 0;
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= n; j ++) {
            if (i == j) continue;
            if (ans < f[(1 << n) - 1][i][j]) {
                ans = f[(1 << n) - 1][i][j];
                cnt = g[(1 << n) - 1][i][j];
            } else if (ans == f[(1 << n) - 1][i][j]) {
                cnt += g[(1 << n) - 1][i][j];
            }
        }
    }
    cout << ans << ' ' << cnt / 2 <<  endl;
}


int main () {
    int T;
    cin >> T;
    while (T --) {
        solve ();
    }

    return 0;
}
posted @ 2025-10-09 21:21  michaele  阅读(10)  评论(0)    收藏  举报