题意:
给一颗树,用最少的点覆盖整棵树。
每一个结点可以防守相邻的一个边,求最少的点防守所有的边。
分析:
1:以当前节点为根节点,在该节点排士兵守护道路的最小消耗。在这种情况下,他的子节点可以安排士兵,也可以不安排士兵。可以从各个子节点两个不同状态(存在士兵,不存在士兵)的最值中选出最小的消耗,然后相加就求出了当前节点派士兵的最小消耗。
2:以当前节点为根节点,不存在士兵。这种情况十分清楚,因为当前节点没有士兵,那么这个节点到子节点之间的道路没有人守护,那么子节点就必须要安排士兵,因此这种情况下。这个节点的最小消耗就是每个子节点存在士兵的情况下的最小消耗的和。
很简单 经典的树形dp, a[]表示当前节点放士兵的可以防守住所有子树的最少士兵,d[]表示当前节点不放士兵的可以防守住所有子树的最少士兵。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#define LL __int64
const int maxn = +;
using namespace std;
int d[maxn], a[maxn], vis[maxn];
vector<int>v[maxn]; void dfs(int root)
{
int i;
vis[root] = ;
int len = v[root].size();
for(i = ; i < len; i++)
{
int tmp = v[root][i];
if(vis[tmp]) continue;
dfs(tmp);
d[root] += a[tmp];
a[root] += min(a[tmp], d[tmp]);
}
a[root] ++;
}
int main()
{
int n, i, x, m, b;
while(~scanf("%d", &n))
{
memset(a, , sizeof(a));
memset(d, , sizeof(d));
memset(vis, , sizeof(vis)); for(i = ; i < n; i++)
v[i].clear();
while(n--)
{
scanf("%d%*c%*c%d%*c", &x, &m);
while(m--)
{
cin>>b;
v[x].push_back(b);
v[b].push_back(x);
}
}
dfs();
cout<<min(d[], a[])<<endl;
}
return ;
}