题目链接:http://poj.org/problem?id=1463
题目大意:给定一棵树,怎样使得占据最少的节点能够监视所有的边
思路:(1)最小点覆盖,二分图匹配;(2)树形DP;这里用树形DP做:用dp[i][0]来表示该点没有放兵,以这个点为根的子树所需的最少兵数;用dp[i][1]来表示该点有放兵,以这个点为根的子树所需的最少兵数。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define maxn 100000
using namespace std;
int dp[maxn][2]; //用DP[I][0]来表示该点没有放兵,以这个点为根的子树所需的最少兵数。
int n; //用DP[I][1]来表示该点有放兵,以这个点为根的子树所需的最少兵数。
int root;
vector<int>node[maxn];
void init()
{
int u,v,num;
root = -1;
for(int i=0;i<=n;i++)
node[i].clear();
for(int i=0;i<n;i++)
{
scanf("%d:(%d)",&u,&num); //记住这种输入读取方式
if(root==-1) root = u;
while(num--)
{
cin>>v;
node[u].push_back(v); //向量添加点的格式
}
}
}
int solve(int fa)
{
dp[fa][0]=0;dp[fa][1]=1;
for(int i=0;i<node[fa].size();i++)
{
int son = node[fa][i];
solve(son); //访问的时候,因为要先知道儿子的信息,所以类似于后续遍历
dp[fa][0]+=dp[son][1]; //如果该父亲不放,那么儿子必须放
dp[fa][1]+=min(dp[son][0],dp[son][1]); //如果该父亲放,儿子在放和不放之间选择最小的
}
return min(dp[fa][0],dp[fa][1]);
}
int main()
{
while(cin>>n)
{
init();
cout<<solve(root)<<endl;
}
return 0;
}