题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1054
题目大意:一棵树,要放置哨兵,要求最少放置多少哨兵能监视到所有的结点
题目分析:
放置哨兵无非两种情况,放或不放,我们可以用dp[i][1]来表示第i个结点放置哨兵,dp[i][0]来表示第i个结点不放置哨兵,我们可以从上往下,从左往右来遍历树,所以这就用到了树形DP的知识,我们很容易知道,如果父亲结点没放哨兵,那么子结点肯定要放置哨兵,如果父亲放置了哨兵,那么子结点可以考虑放或者不放。所以很容易写出状态转移方程dp[v][1] += min(dp[u][1],dp[u][0]),dp[v][0] += dp[u][1],由于子结点可能有多个,我们要依次从左到右遍历所以我们必须开一个brother来存放它前一个兄弟结点,然后只要DFS递归的来从上往下遍历就可以得解了
和我的上一篇差不多:https://www.cnblogs.com/wsy107316/p/11319167.html
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* */ 2 # include <iostream> 3 # include <stdio.h> 4 # include <string.h> 5 # include <cstdlib> 6 # include <cmath> 7 # include <climits> 8 # include <ctime> 9 # include <algorithm> 10 # include <deque> 11 # include <bitset> 12 # include <cctype> 13 # include <queue> 14 # include <stack> 15 # include <list> 16 # include <set> 17 # include <map> 18 # include <vector> 19 using namespace std; 20 const int maxn = 2000; 21 bool vis[maxn]; 22 int dp[maxn][2]; 23 int n; 24 vector<int>son[maxn]; 25 26 void dfs(int root) 27 { 28 vis[root]=1; 29 for(int i=0; i<son[root].size(); i++ ) 30 { 31 int v=son[root][i]; 32 if( !vis[v] ) 33 { 34 dfs(v); 35 dp[root][1] += min(dp[v][0], dp[v][1]);///0表示无哨兵,i处无哨兵则子结点处必须有哨兵 36 dp[root][0] += dp[v][1];///1表示有哨兵,i处有哨兵的情况等于子结点处有哨兵或者无哨兵的最小值 37 } 38 } 39 } 40 41 int main() 42 { 43 int num, so, ss; 44 while( ~ scanf("%d", &n) ) 45 { 46 for(int i=0; i<=n; i++ ) 47 son[i].clear(); 48 memset(vis, 0, sizeof(vis)); 49 50 for(int i=0; i<n; i++ ) 51 { 52 dp[i][0] = 0; 53 dp[i][1] = 1; 54 } 55 56 for(int i=0; i<n; i++ ) 57 { 58 scanf("%d:(%d)", &num, &so); 59 for(int j=0; j<so; j++ ) 60 { 61 scanf("%d", &ss); 62 son[num].push_back(ss);///num节点可以看到ss节点 63 son[ss].push_back(num);///则ss节点也可以看到num结点 64 } 65 } 66 67 dfs(0);///反正整棵树一定是联通的,那么就随便选一个作为根节点 68 cout<<min(dp[0][0], dp[0][1])<<endl; 69 } 70 return 0; 71 }