最小点覆盖P2899

 

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]));
}

 

posted @ 2019-06-16 22:04  paranoid。  阅读(143)  评论(0编辑  收藏  举报