题目大意:给定一棵树,设一个关键节点可以覆盖半径为2的范围,问要覆盖整棵树要至少几个关键节点。
这道题可以用贪心或者树形dp解决。因为最近我深感自己dp能力太弱,所有练练dp。
状态设计:
f[p,0]代表将p的祖父覆盖需要的最少关键点数量
f[p,1]代表将p的父亲覆盖需要的最少数量
f[p,2]代表将p覆盖需要的最少数量
f[p,3]代表将p的儿子覆盖需要的最少数量
f[p,4]代表将p的孙子覆盖需要的最少数量
显而易见,f[p,0]≥f[p,1]≥f[p,2]≥f[p,3]≥f[p,4]
状态转移:
设s为p的一个儿子节点。
$$fleft[ p,0 ight] =sum ^{son}_{s}fleft[ s,4 ight]$$
$$fleft[ p,1 ight] =min left( min left{ fleft[ s,0 ight] +sum ^{son}_{j eq s}fleft[ j,3 ight] ight} ,fleft[ p,0 ight] ight)$$
$$fleft[ p,2 ight] =min left( min left{ fleft[ s,1 ight] +sum ^{son}_{j eq s}fleft[ j,2 ight] ight} ,fleft[ p,1 ight] ight)$$
$$fleft[ p,3 ight] =min left( sum ^{son}_{s}fleft[ s,2 ight] ,fleft[ p,2 ight] ight)$$
$$fleft[ p,4 ight] =min left( sum ^{son}_{s}fleft[ s,3 ight] ,fleft[ p,3 ight] ight)$$
那么最终答案就是f[1,2]了。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <utility> #include <queue> #include <vector> #include <list> #include <set> #include <map> #define MAXN 1010 using namespace std; int n,head[MAXN],to[MAXN*2],nxt[MAXN*2],tot=0,f[MAXN][5]; void connect(int u,int v) { to[++tot]=v;nxt[tot]=head[u];head[u]=tot; } void dfs(int u) { f[u][0]=1;f[u][3]=0;f[u][4]=0; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; dfs(v); f[u][0]+=f[v][4]; f[u][3]+=f[v][2]; f[u][4]+=f[v][3]; } if(head[u]==0) { f[u][1]=f[u][2]=1; return; } for(int i=head[u];i;i=nxt[i]) { int s=to[i]; int f1=f[s][0],f2=f[s][1]; for(int j=head[u];j;j=nxt[j]) { if(j==i) continue; f1+=f[to[j]][3]; f2+=f[to[j]][2]; } f[u][1]=min(f[u][1],f1); f[u][2]=min(f[u][2],f2); } for(int i=1;i<=4;i++) f[u][i]=min(f[u][i],f[u][i-1]); } int main() { memset(f,0x3f,sizeof(f)); scanf("%d",&n); for(int i=2,v;i<=n;i++) { scanf("%d",&v); connect(v,i); } dfs(1); printf("%d ",f[1][2]); return 0; }