BZOJ 1596
本题可以贪心做,如果一个节点他的孩子没被覆盖,并且这个点和他的父亲没覆盖,就覆盖这个点的父亲,这里主要用树形DP来做。
设
$f(u, 0)$为不在$u$建基站,$u$与$u$的子树都有信号
$f(u, 1)$为在$u$建基站,$u$与$u$的子树都有信号
$f(u, 2)$为不在$u$建基站,$u$没有信号,$u$的子树都有信号
以上这种状态是非常常用的
我们看转移(很容易推出来):
$f(u,0)=min(f(j,1)+\sum min(f(son, 0/1)))$
$f(u,1)=\sum min(f(son, 0/1/2))$
$f(u,2)=\sum f(son, 0)$
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
using namespace std;
const int MAXN = 10000 + 5, INF = 100000000;
vector<int> G[MAXN];
int n, f[MAXN][3];
void dp(int u, int p) {
int sum = 0;
f[u][1] = 1, f[u][0] = INF, f[u][2] = 0;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (v != p) {
dp(v, u);
f[u][1] += min(min(f[v][0], f[v][1]), f[v][2]);
f[u][2] += f[v][0];
sum += min(f[v][0], f[v][1]);
}
}
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (v != p) {
f[u][0] = min(f[u][0], f[v][1] + sum - min(f[v][0], f[v][1]));
}
}
}
void clean() {
for (int i = 0; i <= n; i++) G[i].clear();
}
void solve() {
clean();
for (int i = 1; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
G[x].push_back(y), G[y].push_back(x);
}
dp(1, 0);
printf("%d\n", min(min(f[1][0], f[1][1]), f[1][2] + 1));
}
int main() {
scanf("%d", &n), solve();
return 0;
}