题目大意:给定一棵树,设一个关键节点可以覆盖半径为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的一个儿子节点。
$$f\left[ p,0\right] =\sum ^{son}_{s}f\left[ s,4\right]$$
$$f\left[ p,1\right] =\min \left( \min \left\{ f\left[ s,0\right] +\sum ^{son}_{j\neq s}f\left[ j,3\right] \right\} ,f\left[ p,0\right] \right)$$
$$f\left[ p,2\right] =\min \left( \min \left\{ f\left[ s,1\right] +\sum ^{son}_{j\neq s}f\left[ j,2\right] \right\} ,f\left[ p,1\right] \right)$$
$$f\left[ p,3\right] =\min \left( \sum ^{son}_{s}f\left[ s,2\right] ,f\left[ p,2\right] \right)$$
$$f\left[ p,4\right] =\min \left( \sum ^{son}_{s}f\left[ s,3\right] ,f\left[ p,3\right] \right)$$
那么最终答案就是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\n",f[1][2]); return 0; }