题目传送门:https://www.luogu.org/problemnew/show/P2279
题意:
有一棵树,求覆盖最小数量的点覆盖全树所有的点,注意:每个点可覆盖于它距离不大于2的点。
思路:
题目都说树,肯定用树形dp。因为覆盖距离不大于2的点,可知需要有儿子,孙子的状态(详见代码注释)。
代码:
#include<cstdio>
#include<algorithm>
#define INF 2147483647
using namespace std;
int n,len=0;
struct node{int x,y,next;} a[2000];
int last[2000],f[2000][10];
/*
f[x][0]表示选取x节点
f[x][1]表示至少选取了x的一个儿子
f[x][2]表示至少选取了x的一个孙子
前三种情况x和整棵子树一定被覆盖到
f[x][3]表示x的所有儿子全部被覆盖
f[x][4]表示x的所有孙子全部被覆盖
后两种情况x不一定被覆盖到,但除x外的整棵子树一定被覆盖到
化简得f[i][k]表示min(f[i][0],f[i][1]....f[i][k])且k>=2(因为上述转移方程最少都是0~2状态)
*/
void ins(int x,int y)
{
a[++len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;
}
void dfs(int x)
{
int t1=INF,t2=INF;
f[x][0]=1;
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
dfs(y);
f[x][0]+=f[y][4];
f[x][3]+=f[y][2];
f[x][4]+=f[y][3];
t1=min(t1,f[y][0]-f[y][3]);
t2=min(t2,f[y][1]-f[y][2]);
}
f[x][1]=min(f[x][4]+t1,f[x][0]);
f[x][2]=min(f[x][1],f[x][3]+t2);
f[x][3]=min(f[x][2],f[x][3]);
f[x][4]=min(f[x][3],f[x][4]);
}
int main()
{
int x;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%d",&x);
ins(x,i);
}
dfs(1);
printf("%d",f[1][2]);
}