题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1054
思路:树形DP,用二分匹配也能解决
定义dp[root][1],表示以root 为根结点的子树且在root放一个士兵的最优值,那么因为在root已经有士兵了,所以对其孩子来说可以放也可以不放
那么dp[root][1]+=min(dp[son][1],dp[son][0]);
dp[root][0]表示以root为根结点的子树且在root没有放士兵的最优值,那么因为在root没有士兵,所以在其孩子节点一定要放一个士兵,才能保证与其相连的边被观察到,
那么dp[root][0]+=dp[son][1];
最后的答案即为min(dp[0][1],dp[0][0]);(我是以0为整颗树的根节点的);
状态转移方程草写出来以后,实现就比较简单了吧。。
代码如下:

1 #include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 using namespace std; 6 #define MAX 1510 7 int dp[MAX][MAX]; 8 int n; 9 class node 10 { 11 public: 12 int to; 13 int next; 14 }; 15 node edge[MAX*3]; 16 int head[MAX]; 17 int vis[MAX]; 18 int tol; 19 void init() 20 { 21 memset(dp,0,sizeof(dp)); 22 memset(head,-1,sizeof(head)); 23 memset(vis,0,sizeof(vis)); 24 tol=0; 25 } 26 void Build_Tree(int u,int v) 27 { 28 edge[tol].to=v; 29 edge[tol].next=head[u]; 30 head[u]=tol++; 31 32 } 33 void input() 34 { 35 int u,v,num; 36 char c; 37 for(int k=1;k<=n;k++) 38 { 39 cin>>u>>c>>c>>num>>c; 40 for(int i=1;i<=num;i++) 41 { 42 cin>>v; 43 Build_Tree(u,v); 44 Build_Tree(v,u); 45 } 46 } 47 } 48 void dfs(int root) 49 { 50 vis[root]=1; 51 for(int i=head[root];i!=-1;i=edge[i].next) 52 { 53 if(vis[edge[i].to]) continue; 54 int son=edge[i].to; 55 dfs(son); 56 57 dp[root][0]+=dp[son][1]; 58 dp[root][1]+=min(dp[son][1],dp[son][0]); 59 } 60 dp[root][1]+=1; 61 } 62 int main() 63 { 64 while(scanf("%d",&n)!=EOF) 65 { 66 init(); 67 input(); 68 dfs(0); 69 cout<<min(dp[0][1],dp[0][0])<<endl; 70 } 71 return 0; 72 }