最小点覆盖P2899
#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=1e5+5;
vector<int> v[maxn];
int vis[maxn];
int f[maxn][3];
//f(i,0)i点放置灯,以i为根结点的子树所产生的满足条件的最小总数 f(i,0)+=min(f(i,0),f(i,1),f(i,2))
//解释:如果i放了,那么它的方案可由它子节点没放灯所产生的满足条件的最优解,
//子节点放了灯所产生的满足条件的最优解,和要父亲放了灯才产生的满足条件的最优解的三种选其一(最小)转移过来。
//f(i,1)i的子结点放置灯,i为根结点的子树所产生的满足条件的最小总数f(i,1)+=min(f(i,0),f(i,1))
//如果i没放,那么它的子节点要有一个放的,同时还要满足放的灯最少,那么我们可以这样考虑:
//对于它的每个子节点我们都从子结点放或不放中选择最小值,肯定是最小数,但如果子节点都选择了不放也就是f(j,1)是不行的,
//所以特判一下,如果子节点都是选择f(i,1),我们再从所有子节点中挑一个最小的改变也就是最小的一个f(j,0)中计算f(i,1)+=(-f[i,1]+f[i,0])最
//f(i,2)i的父节点放置灯,i为根结点的子树所产生的满足条件的最小总数
//这是要求父节点必须放置,那么它可放可不放,从min(f(j,0),f(j,1))转移
const int INF=0x3f3f3f3f;
void dfs(int x)
{
vis[x]=1;
f[x][0]=1;
int flag=0;
int fy=INF;
int flag1=0;
for(int i=0; i<v[x].size(); i++)
{
int y=v[x][i];
if(vis[y]) continue;
flag1=1;
dfs(y);
f[x][0]+=min(f[y][0],min(f[y][1],f[y][2]));
if(f[y][0]<=f[y][1])
{
flag=1;
f[x][1]+=f[y][0];
}
else
{
f[x][1]+=f[y][1];
if((f[y][0]-f[y][1])<fy) // 选择差值最小进行贪心~
{
fy=f[y][0]-f[y][1];
}
}
f[x][2]+=min(f[y][0],f[y][1]);
}
if(flag==0)
f[x][1]+=fy;
if(flag1==0)
{
f[x][0]=1;
f[x][1]=INF;
f[x][2]=0;
}
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n-1; i++)
{
int a,b;
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
dfs(1);
printf("%d",min(f[1][0],f[1][1]));
}