题目地址:
https://www.acwing.com/problem/content/325/
鲍勃喜欢玩电脑游戏,特别是战略游戏,但有时他找不到解决问题的方法,这让他很伤心。现在他有以下问题。他必须保护一座中世纪城市,这条城市的道路构成了一棵树。每个节点上的士兵可以观察到所有和这个点相连的边。他必须在节点上放置最少数量的士兵,以便他们可以观察到所有的边。你能帮助他吗?
输入格式:
输入包含多组测试数据,每组测试数据用以描述一棵树。对于每组测试数据,第一行包含整数 N N N,表示树的节点数目。接下来 N N N行,每行按如下方法描述一个节点。
节点编号:(子节点数目) 子节点 子节点 …
节点编号从 0 0 0到 N − 1 N−1 N−1,每个节点的子节点数量均不超过 10 10 10,每个边在输入数据中只出现一次。
输出格式:
对于每组测试数据,输出一个占据一行的结果,表示最少需要的士兵数。
数据范围:
0 < N ≤ 1500 0<N≤1500 0<N≤1500
思路是动态规划。设 f [ i ] [ 0 , 1 ] f[i][0,1] f[i][0,1]分别是以 i i i为根的子树中,不取 i i i和取 i i i两种情况下,分别需要的最少士兵数。设 i i i的孩子分别是 x 1 , . . . , x n x_1,...,x_n x1,...,xn,当求 f [ i ] [ 0 ] f[i][0] f[i][0]的时候,由于 i i i是不取的,那么其所有孩子都必须取,以保证 ( i , x 1 , . . . , n ) (i,x_{1,...,n}) (i,x1,...,n)这些边是覆盖的,所以有: f [ i ] [ 0 ] = ∑ 1 ≤ k ≤ n f [ x k ] [ 1 ] f[i][0]=\sum_{1\le k\le n} f[x_k][1] f[i][0]=1≤k≤n∑f[xk][1]当求 f [ i ] [ 0 ] f[i][0] f[i][0]的时候,由于 i i i是取的,所以其每个孩子可取可不取,所以有: f [ i ] [ 1 ] = 1 + ∑ 1 ≤ k ≤ n max { f [ x k ] [ 0 ] , f [ x k ] [ 1 ] } f[i][1]=1+\sum _{1\le k\le n}\max\{f[x_k][0],f[x_k][1]\} f[i][1]=1+1≤k≤n∑max{ f[xk][0],f[xk][1]}代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1510;
int n;
int h[N], e[N], ne[N], idx;
int f[N][2];
bool is_root[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
int c0 = 0, c1 = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
dfs(j);
c0 += f[j][1];
c1 += min(f[j][0], f[j][1]);
}
f[u][0] = c0;
f[u][1] = c1;
}
int main() {
while (scanf("%d", &n) != -1) {
// 重置一下各个数组
memset(h, -1, sizeof h);
memset(is_root, true, sizeof is_root);
memset(f, 0, sizeof f);
idx = 0;
for (int i = 0; i < n; i++) {
int id, cnt;
scanf("%d:(%d)", &id, &cnt);
for (int j = 0; j < cnt; j++) {
int a;
cin >> a;
add(id, a);
is_root[a] = false;
}
}
for (int i = 0; i < n; i++)
if (is_root[i]) {
dfs(i);
cout << min(f[i][0], f[i][1]) << endl;
break;
}
}
return 0;
}
每组数据时空复杂度 O ( N ) O(N) O(N)。