题目链接 Round 322 Problem F
题意 给定一棵树,保证叶子结点个数为$2$(也就是度数为$1$的结点),现在要把所有的点染色(黑或白)
要求一半叶子结点的颜色为白,一半叶子结点的颜色为黑,求边权和的最小值。
若一条边连接的两个点颜色不一样,则该条边边权为$1$,否则为$0$。
考虑树型$DP$。
$f[x][i][j]$表示当以$x$为根的子树中有$i$个叶子结点染成黑色并且$x$的颜色为$j$的时候边权和的最小值。
这道题计算$size$的时候只考虑叶子结点,不考虑除叶子结点以外的结点。
$DP$的时候叶子结点和其他结点分开考虑,注意状态转移的时候要开个过渡数组$g$,否则在状态转移过程中出现的最小值可能会被错误地保存到最后。
时间复杂度$O(n^{2})$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef long long LL; const int N = 5e3 + 10; int f[N][N][2], sz[N], in[N]; int n, all, root; int g[N][2]; vector <int> v[N]; void up(int &a, int b){ a = min(a, b);} void dfs(int x, int fa){ if (in[x] == 1){ sz[x] = 1; f[x][1][1] = 0; f[x][0][0] = 0; return; } f[x][0][0] = f[x][0][1] = 0; for (auto u : v[x]){ if (u == fa) continue; dfs(u, x); memset(g, 0x3f3f3f3f, sizeof g); dec(i, sz[x], 0){ dec(j, sz[u], 0){ up(g[i + j][1], f[x][i][1] + f[u][j][1]); up(g[i + j][1], f[x][i][1] + f[u][j][0] + 1); up(g[i + j][0], f[x][i][0] + f[u][j][1] + 1); up(g[i + j][0], f[x][i][0] + f[u][j][0]); } } memcpy(f[x], g, sizeof f[x]); sz[x] += sz[u]; } } int main(){ scanf("%d", &n); rep(i, 2, n){ int x, y; scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x); ++in[x]; ++in[y]; } if (n == 2) return 0 * puts("1"); rep(i, 1, n) if (in[i] == 1) ++all; else root = i; memset(f, 0x3f3f3f3f, sizeof f); dfs(root, 0); printf("%d\n", min(f[root][all / 2][0], f[root][all / 2][1])); return 0; }