题目简述:
有若干结点,结点之间有路相连,构成树形结构,如果在一个结点上放置一个士兵,与这个结点相连的路就可以被监视,现在要监视所以的路,问至少要多少士兵。
思路:
最优解结构: dp[i][0],dp[i][1]分别表示不在i结点和在i结点上放置士兵时整个以i结点为根的子树被覆盖用到目标的最少数量。
状态转移:对叶子结点,有
dp[i][0]=0,dp[i][1]=1;
对非叶子结点,有
dp[i][0]=∑(dp[j][1])
dp[i][1]=∑(min(dp[j][0],dp[j][1]))+1 (j为i的子结点)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define maxn 1508
using namespace std;
int childcnt[maxn], pre[maxn], dp[maxn][2], n;
//dp过程
void DP(int root) {
int i, d1 = 0, d0 = 0;
if(childcnt[root] == 0) {
dp[root][0] = 0;
dp[root][1] = 1;
return;
}
for(i = 0; i < n; i++)
if(pre[i] == root) {
DP(i);
d1 += min(dp[i][0], dp[i][1]);
d0 += dp[i][1];
}
dp[root][1] = d1+1;
dp[root][0] = d0;
}
int main() {
int i, dad, child, m;
while(~scanf("%d", &n)) {
memset(childcnt, -1, sizeof(childcnt));
memset(pre, -1, sizeof(pre));
int root = -1;
for(i = 0; i < n; i++) {
scanf("%d:(%d)", &dad, &m);
childcnt[dad] = m;//记录dad儿子的个数
if(root == -1)
root = dad;
while(m--) {
scanf("%d", &child);
pre[child] = dad;//记录child的父亲结点
}
if(pre[root] == dad)
root = dad; //找到根结点
}
DP(root);
cout<<min(dp[root][0], dp[root][1])<<endl;
}
return 0;
}