Loading

P4362 [NOI2002] 贪吃的九头龙

题意

给定 \(n\) 个点的树,有边权。将 \(n\) 个点染成 \(m\) 种颜色,其中 \(1\) 号颜色必须染色 \(k\) 个结点,且 \(1\) 号结点必须染成 \(1\) 号颜色。剩下的 \(m - 1\) 中颜色分别至少出现 \(1\) 次。

一条边的两个结点染色相同的时候,贡献边权,否则贡献为 \(0\)

问:染色达到要求的最小边权和。

思路

首先,因为点 1 要然成一种颜色且要染 \(k\) 个点,所以还有 \(m - 1\) 种颜色,\(n - k\) 个点要染色,如果 \(n - k < m - 1\),那么一定不能染色成功,输出 \(-1\)

\(f_{i, j, k}\) 表示到点 \(i\),染了和点 1 的颜色一样的点的颜色个数,这个点是否染和点 1 颜色一样的颜色个数可以获得最小贡献。

如果 \(m > 2\),这时候,如果都不染和点 1 一样的颜色,颜色不会重合,第一行式子中不用 + w:

\(f_{u, j, 0} = \min(f_{u, j, 0}, f_{u, j - k, 0} + f_{to, k, 0}, f_{u, j - k, 0} + f_{to, k, 1})\)
\(f_{u, j, 1} = \min(f_{u, j, 1}, f_{u, j - k, 1} + f_{to, k, 1} + w, f_{u, j - k, 1} + f_{to, k, 0})\)

如果 \(m = 2\),如果都不染和点 1 一样的颜色,颜色会重合:

\(f_{u, j, 0} = \min(f_{u, j, 0}, f_{u, j - k, 0} + f_{to, k, 0} + w, f_{u, j - k, 0} + f_{to, k, 1})\)
\(f_{u, j, 1} = \min(f_{u, j, 1}, f_{u, j - k, 1} + f_{to, k, 1} + w, f_{u, j - k, 1} + f_{to, k, 0})\)

(仔细对比一下)

然后就是一定要用临时数组,请看我发的帖子

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 310;

struct edge {
    int to, next, w;
} e[N * 2];

int head[N], idx = 1;

void add(int u, int v, int w) {
    idx++;
    e[idx].to = v;
    e[idx].next = head[u];
    e[idx].w = w;
    head[u] = idx;
}

int n, m, a;
int f[N][N][2], g[N][2], sz[N];

void dfs(int u, int fa) {
    sz[u] = 1;
    f[u][1][1] = f[u][0][0] = 0;
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        dfs(to, u);
        sz[u] += sz[to];
        memset(g, 0x3f, sizeof(g));
        for (int j = 0; j <= min(a, sz[u]); j++) {
            for (int k = 0; k <= min(j, sz[to]); k++) {
                g[j][0] = min(g[j][0], min(f[u][j - k][0] + f[to][k][0] + (m == 2) * e[i].w, f[u][j - k][0] + f[to][k][1]));
                g[j][1] = min(g[j][1], min(f[u][j - k][1] + f[to][k][1] + e[i].w, f[u][j - k][1] + f[to][k][0]));
            }
        }
        memcpy(f[u], g, sizeof(g));
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> m >> a;
    if (n - a < m - 1) {
        cout << "-1\n";
        return 0;
    }

    for (int i = 1; i < n; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
    }
    memset(f, 0x3f, sizeof(f));
    dfs(1, 0);
    cout << f[1][a][1] << '\n';
    return 0;
}
posted @ 2024-08-12 11:23  SunnyYuan  阅读(2)  评论(0编辑  收藏  举报