「Bzoj 1596」「Usaco2008 Jan」电话网络 (树形DP/贪心)

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;
}
------ 本文结束 ------