【银川网络赛G】Factories

题目大意:给定一棵 N 个节点的树,边有边权,选定 M 个叶子节点,使得任意两个叶子节点的树上距离之和最小,求最小值是多少。

题解:任意两点的树上距离和问题应从边的贡献角度考虑。
\(f[u][i]\) 表示以 u 为根的子树中,选了 i 个叶子节点的最优解,状态转移方程为:
\[ f[u][i + j] = min(f[u][i + j], f[u][i] + f[v][j] + w * j * (j - m)) \]
其中所加项为子节点和父节点之间的边的贡献。

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

int main() {
    int T, kase = 0;
    scanf("%d", &T);
    while (T--) {
        int n, m;
        scanf("%d %d", &n, &m);
        vector<vector<pair<int, LL>>> adj(n + 1); // <to, w>
        vector<int> deg(n + 1);
        for (int i = 1; i < n; i++) {
            int x, y, z;
            scanf("%d %d %d", &x, &y, &z);
            adj[x].emplace_back(y, z);
            adj[y].emplace_back(x, z);
            ++deg[x], ++deg[y];
        }
        if (m == 1) {
            printf("Case #%d: 0\n", ++kase);
            continue;
        }
        if (n == 2) {
            printf("Case #%d: %lld\n", ++kase, adj[1][0].second);
            continue;
        }
        vector<vector<LL>> f(n + 1, vector<LL>(m + 1, 1e12));
        function<int(int, int)> dfs = [&](int u, int fa) -> int {
            bool not_leaf = 0;
            int sz = 0;
            f[u][0] = 0;
            for (auto t : adj[u]) {
                int v = t.first;
                LL w = t.second;
                if (v == fa) {
                    continue;
                }
                not_leaf = 1;
                int son = dfs(v, u);
                for (int i = min(sz, m); i >= 0; i--) {
                    for (int j = min(son, m - i); j >= 0; j--) {
                        if (i + j > m) {
                            continue;
                        }
                        f[u][i + j] = min(f[u][i + j], f[u][i] + f[v][j] + (LL)j * (m - j) * w);
                    } 
                }
                sz += son;
            }
            if (not_leaf == 0) {
                f[u][1] = 0;
                sz = 1;
            }
            return sz;
        };
        int rt = 0;
        for (int i = 1; i <= n; i++) {
            if (deg[i] > 1) {
                rt = i;
                break;
            }
        }
        dfs(rt, 0);
        printf("Case #%d: %lld\n", ++kase, f[rt][m]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wzj-xhjbk/p/11455472.html