题目描述
Bob喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的办法。现在他有个问题。
他要建立一个古城堡,城堡中的路形成一棵树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。
注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。
请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵.
输入格式
第一行 $N$ ,表示树中结点的数目。
第二行至第 $N+1$ 行,每行描述每个结点信息,依次为:该结点标号 $i$ ,$k$ (后面有 $k$ 条边与结点 $I$ 相连)。
接下来 $k$ 个数,分别是每条边的另一个结点标号 $r_1$,$r_2$,…,$r_k$。
对于一个 $n$ ( $0 < n \leq 1500 $ )个结点的树,结点标号在 $0$ 到 $n-1$ 之间,在输入数据中每条边只出现一次。
输出格式
输出文件仅包含一个数,为所求的最少的士兵数目。
样例输入
4
0 1 1
1 2 2 3
2 0
3 0
样例输出
1
解题思路
考虑树形 $DP$,本题和没有上司的舞会非常相似
令数组 $f[i][0/1]$ 表示节点 $i$ 上放置士兵或不放置士兵时完全覆盖节点 $i$ 的子树的最小士兵数目
那么
$$f[i][0] \leftarrow f[v][1]$$
$$
f[i][1] \leftarrow (f[v][0], f[v][1])_{min}
$$
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 1500 + 10;
const int MAXM = 3000 + 10;
inline int read(){
int x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
ch = getchar();
while('0' <= ch && ch <= '9'){
x = x*10 + ch - '0';
ch = getchar();
}
return x;
}
int Head[MAXN], to[MAXM], Next[MAXM], tot = 1;
int f[MAXN][2];
inline void dfs(int x, int fa){
f[x][1] = 1;
for(register int i=Head[x]; i; i=Next[i]){
int v = to[i];
if(v != fa){
dfs(v, x);
f[x][0] += f[v][1];
f[x][1] += min(f[v][0], f[v][1]);
}
}
}
inline void add(int a, int b){
to[tot] = b;
Next[tot] = Head[a];
Head[a] = tot++;
}
int n;
int main(){
n = read();
for(register int i=1; i<=n; i++){
int id = read();
int k = read();
for(register int j=0; j<k; j++){
int v = read();
add(id, v);
add(v, id);
}
}
dfs(0, -1);
printf("%d", min(f[0][0], f[0][1]));
return 0;
}