题意:
给定2≤N≤5000的一棵树,现在要将这棵树黑白染色
染色的要求是:叶子节点必须黑白对半(保证有偶数个叶子)
在满足染色要求的情况下,使得不同色节点连接的边数最少
求这个边数
分析:
树形dp
一开始定义状态dp[u][cnt][2]:=u这个节点染成0/1,整棵树有cnt个染成1的点,的最少不同色节点连接的边
发现根本没法转移,太年轻,树形dp的状态怎么能跟子树没关系呢。。
f[u][cnt][2]:=以u为根的这颗子树有cnt染成1的叶子,且u被染成0/1,的最少不同色节点连接的边
leaves[u]:=以u为根的这颗子树有多少个叶子
转移的时候,叶子节点显然f[u][1][1]=f[u][0][0]=0,leaves[u]=1
对于其他情况,只能暴力枚举染1色的叶子数,但要注意枚举u的染1色叶子数的时候,不能计算当前v这颗子树的叶子
由于需要枚举所有u的儿子,我们需要一个新的状态来帮助转移
g[0][cnt][2]:=只有u本身的情况,染成0/1,显然g[0][0][0]=g[0][0][1]=0
g[1][cnt][2]:=当前u的状态下,u的第1个儿子对u的转移
显然g我们可以滚动数组来对接受所有儿子的转移,最终得到整个u为根的子树的结果
将结果复制过去,f[u]=g[cur]
halfLeaves=leaves[root]/2, 显然ans=min(f[root][halfLeaves][0],f[root][halfLeaves][1])
所谓的根只要找到随便一个不是叶子的即可
显然我们需要特判n=2时,找不到根,ans=1
对于时间复杂度为啥是O(n2),题解说的是每对(x,y)对其总更新次数只在lca出贡献一次
![]()
表示不懂,不过看起来确实复杂度比O(n3)小
代码:
//
// Created by TaoSama on 2016-02-02
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 5e3 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n;
int f[N][N][2], g[2][N][2], leaves[N]; //leaf to 1
vector<int> G[N];
void getMin(int &x, int y) {
if(x > y) x = y;
}
void see(int u) {
printf("%d:\n", u);
for(int i = 0; i <= leaves[u]; ++i)
for(int j = 0; j < 2; ++j)
printf("f[%d][%d]=%d\n", i, j, f[u][i][j]);
puts("");
}
void dfs(int u, int fa) {
leaves[u] = 0;
if(G[u].size() == 1) {
leaves[u] = 1;
f[u][0][0] = f[u][1][1] = 0;
f[u][0][1] = f[u][1][0] = INF;
// see(u);
return;
}
for(int v : G[u]) {
if(v == fa) continue;
dfs(v, u);
}
int p = 0;
g[p][0][0] = g[p][0][1] = 0;
for(int v : G[u]) {
if(v == fa) continue;
memset(g[!p], 0x3f, sizeof g[!p]);
for(int i = 0; i <= leaves[u]; ++i)
for(int j = 0; j <= leaves[v]; ++j)
for(int a = 0; a < 2; ++a)
for(int b = 0; b < 2; ++b)
getMin(g[!p][i + j][a], g[p][i][a] + f[v][j][b] + (a != b));
p = !p;
leaves[u] += leaves[v];
}
memcpy(f[u], g[p], sizeof g[p]);
// see(u);
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d", &n) == 1) {
for(int i = 1; i <= n; ++i) G[i].clear();
for(int i = 1; i < n; ++i) {
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
if(n == 2) {puts("1"); continue;}
for(int i = 1; i <= n; ++i) {
if(G[i].size() > 1) {
// printf("root:%d\n", i);
dfs(i, -1);
int cnt = leaves[i] >> 1;
int ans = min(f[i][cnt][0], f[i][cnt][1]);
printf("%d\n", ans);
break;
}
}
}
return 0;
}