http://poj.org/problem?id=1463
题意:有一棵n个结点的树,要在这棵树上放士兵守卫,一个士兵可以守卫自己所在的位置以及与之相邻的点。问最少放多少个士兵?
题解:对于每个点,两种情况,放与不放,放则计数1,不放则计数0。
对于某个点x
如果不放,则与他相邻的点必然要放,否则谁来守卫点x?
如果放,则与他相邻的点可放可不放。
首先要造一棵树,从根节点对所有儿子节点深搜。
dp[i][0]和dp[i][1]表示 包括i本身 和 以i为父节点的子孙结点的摆放数 的累计最小值。
对于样例1,可以造出如下这样的树,0是根结点,指向儿子。
1 #include<stdio.h> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<math.h> 6 #include<string> 7 #include<map> 8 #include<queue> 9 #include<stack> 10 #include<set> 11 #define ll long long 12 #define inf 0x3f3f3f3f 13 using namespace std; 14 15 int n,m; 16 int dp[10086][2]; 17 vector<int>son[10086]; 18 bool vis[10086]; 19 20 void dfs(int x) 21 { 22 dp[x][0]=0; 23 dp[x][1]=1;///每次进入深搜,初始化这个点是可以选或者不选 24 for(int i=0;i<son[x].size();i++) 25 { 26 int y=son[x][i]; 27 dfs(y); 28 dp[x][0]+=dp[y][1]; ///如果不选,那么谁来守卫?那儿子就需要守卫 29 dp[x][1]+=min(dp[y][0],dp[y][1]);///如果选择守卫,则儿子可以选择守卫或者不守卫 30 } 31 } 32 33 int main() 34 { 35 while(scanf("%d",&n)!=EOF) 36 { 37 for(int i=0;i<n;i++) 38 son[i].clear(); 39 memset(vis,false,sizeof(vis)); 40 memset(dp,0,sizeof(dp)); 41 for(int i=0;i<n;i++) 42 { 43 int x,y; 44 scanf("%d:(%d)",&x,&m); 45 while(m--) 46 { 47 scanf("%d",&y); 48 son[x].push_back(y); 49 vis[y]=true;///y有父亲,不能作为根结点 50 } 51 } 52 for(int i=0;i<n;i++) 53 { 54 if(!vis[i])///找到没有儿子的结点作为根节点 55 { 56 dfs(i); 57 printf("%d\n",min(dp[i][0],dp[i][1]));///取根节点的最小情况 58 break; 59 } 60 } 61 } 62 return 0; 63 }
/** dfs(0) { dp[0][0]=0;dp[0][1]=1; for(int i=0;i<1;i++) { y=1; dfs(1) { dp[1][0]=0;dp[1][1]=1; for(int i=0;i<2;i++) { now i=0; y=2; dfs(2); { dp[2][0]=0;dp[2][1]=1; } dp[1][0]=dp[1][0]+dp[2][1]=0+1=1; dp[1][1]+=min(dp[2][0],dp[2][1])=1+0=1; now i=1; y=3; dfs(3); { dp[3][0]=0;dp[3][1]=1; } dp[1][0]=dp[1][0]+dp[3][1]=1+1=2; dp[1][1]+=min(dp[3][0],dp[3][1])=1+0=1; } dp[0][0]=dp[0][0]+dp[1][1]=0+1=1; dp[0][1]+=min(dp[1][0],dp[1][1])=1+1=2; } } } */