题意:给一棵树,当在某个结点上放置士兵时,与该点相邻的边就被看守住了,求最少需多少士兵才能看住树中所有边。
分析:先将无根树有根化,然后DP。
状态设计:
1、dp[i][0],表示在结点 i 没放置士兵的情况下,看住以结点 i 为根的子树的所有边所需的最少士兵;
2、dp[i][1],表示在结点 i 放置士兵的情况下,看住以结点 i 为根的子树的所有边所需的最少士兵。
状态转移:
1、dp[i][0]=∑dp[j][1],j 是 i 的儿子结点;(根结点不放士兵时,与其相连的边必须由儿子结点来看守)
2、dp[i][1]=1+∑ ( MIN ( dp[j][0] , dp[j][1] ) ),j 是 i 的儿子结点。 (根结点放士兵时,儿子结点可放可不放)
初始化: d[i][0]=0,d[i][1]=1,其中 i 是叶子结点
View Code
#include <stdio.h> #include <string.h> #include <vector> using namespace std; #define N 1510 #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b)) int n,p[N],d[N],maxd; vector<int>g[N]; vector<int>rd[N]; int dp[N][2]; void init() { int i; maxd=0; for(i=0; i<n; i++) { g[i].clear(); rd[i].clear(); } memset(dp,0,sizeof(dp)); } void read() { int i,j,k,cnt; for(k=0; k<n; k++) { scanf("%d:(%d)",&i,&cnt); while(cnt--) { scanf("%d",&j); g[i].push_back(j); g[j].push_back(i); } } } void dfs(int u,int fa) { int i,v; d[u]=(fa==-1)?0:d[fa]+1; rd[d[u]].push_back(u); maxd=MAX(maxd,d[u]); for(i=0; i<g[u].size(); i++) { v=g[u][i]; if(v^fa) dfs(v,p[v]=u); } } void DP() { int i,j,u; for(i=maxd; i>=0; i--) { for(j=0; j<rd[i].size(); j++) { u=rd[i][j]; dp[u][1]+=1; if(i) { dp[p[u]][0]+=dp[u][1]; dp[p[u]][1]+=MIN(dp[u][0],dp[u][1]); } } } printf("%d\n",MIN(dp[0][0],dp[0][1])); } int main() { int i,j,k; while(~scanf("%d",&n)) { init(); read(); dfs(0,-1); DP(); } return 0; }