题意:
给定一棵树,你可以在结点上安置士兵,如果当前节点布置上了士兵,那么与此节点相连的边就都是安全的。
问最少需要给多少个节点安置士兵。
题解:
树形DP,转移方程:
root代表当前结点,child代表其子结点,0代表此结点未安置士兵,1代表安置士兵。
![]()
如果此结点未安置士兵,那么与此结点相连的所有节点必须安置士兵。
(这样才能保证与当前结点相连的边都变得安全)
如果此结点安置了士兵,那么子节点可以安置也可以不安置,取最小值。用DFS递归先求得子节点的dp值,由子节点推出父节点。
代码:
#include <set> #include <map> #include <cmath> #include <stack> #include <queue> #include <vector> #include <string> #include <cstdio> #include <cstring> #include <sstream> #include <iomanip> #include <iostream> #include <algorithm> #define clr(str,x) memset(str,x,sizeof(str)) #define FRER() freopen("in.txt","r",stdin); #define FREW() freopen("out.txt","w",stdout); #define MAX_INF 0x7fffffff #define INF 0x3f3f3f3f #define maxn 1510 typedef long long int ll; using namespace std; vector<int> tree[maxn]; int f[maxn],n,dp[maxn][2]; void dfs(int root) { dp[root][0]=0; dp[root][1]=1; for(int i=0; i<tree[root].size(); i++) { dfs(tree[root][i]); dp[root][0]+=dp[tree[root][i]][1]; dp[root][1]+=min(dp[tree[root][i]][1],dp[tree[root][i]][0]); } } int main() { //FRER() //FREW() int edge,fa,son; while(scanf("%d",&n)!=EOF) { clr(f,-1); clr(dp,0); for(int i=1; i<=n; i++) { scanf("%d:(%d)",&fa,&edge); tree[fa].clear(); while(edge--) { scanf("%d",&son); tree[fa].push_back(son); f[son]=fa; } } //寻找根节点(没有父节点的即为根节点) int root; for(int i=0; i<n; i++) { if(f[i]==-1) { root=i; break; } } dfs(root); printf("%d\n",min(dp[root][1],dp[root][0])); } return 0; }