题目大意:
给出一颗n个节点的树(也就是有n-1条边),请选出最小点数使得所有点被覆盖掉(每个节点可以被自己覆盖,也可以被父亲覆盖,还可以被儿子覆盖)
分析:
好难啊( ⊙ o ⊙ )啊!
f[i][0]代表选择i节点,那么i和i的儿子都被覆盖
f[i][1]代表不选i节点,但是i被儿子覆盖
f[i][2]代表不选i节点,并且i不被儿子覆盖,但是儿子已经被覆盖
f[i][0]+=min(f[to[i]][0],f[to[i]][1],f[to[i]][2])
f[i][2]+=min(f[to[i]][0],f[to[i]][1])
这两个很容易理解
那么f[i][1]怎么转移呢(⊙o⊙)?
我们要保证至少有一个儿子是选择的
所以我们选择k点
f[i][1]=f[k][0]+sum((f[to[i]!=k][1],f[to[i]!=k][0]))
然后O(n)的枚举k点即可O(∩_∩)O哈!
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x3f3f3f3f
#define MIN(a,b,c) min(a,min(b,c))
using namespace std;
const int maxn=10000+5;
int n,hd[maxn],to[maxn*2],nxt[maxn*2],cnt,f[maxn][3];
void add(int x,int y){
to[cnt]=y;
nxt[cnt]=hd[x];
hd[x]=cnt++;
}
void dfs(int root,int fa){
int sum=0;
f[root][0]=1;//root is chosen and root&root's son are covered
f[root][1]=inf;//root is not chosen and root&root's son are covered
f[root][2]=0;//root is not chosen and root si not covered ,root's son is covered
for(int i=hd[root];i!=-1;i=nxt[i]){
if(to[i]==fa)
continue;
dfs(to[i],root),sum+=min(f[to[i]][1],f[to[i]][0]);
f[root][0]+=MIN(f[to[i]][0],f[to[i]][1],f[to[i]][2]);
f[root][2]+=min(f[to[i]][0],f[to[i]][1]);
}
for(int i=hd[root];i!=-1;i=nxt[i]){
if(to[i]==fa)
continue;
f[root][1]=min(f[root][1],f[to[i]][0]+sum-min(f[to[i]][0],f[to[i]][1]));
}
}
signed main(void){
memset(hd,-1,sizeof(hd));
scanf("%d",&n),cnt=0;
for(int i=1,x,y;i<n;i++)
scanf("%d%d",&x,&y),add(x,y),add(y,x);
memset(f,inf,sizeof(f));
dfs(1,-1);
cout<<min(f[1][0],f[1][1])<<endl;
return 0;
}
by >o< neighthorn