hdu5758 Explorer Bo 树形dp 最小链覆盖

来源:互联网 发布:照片马赛克软件 编辑:程序博客网 时间:2024/06/09 16:22

题目:给你一棵树,用最少的链去覆盖这棵树,求链的最小总长度。

解析:num为叶子节点数,显然链数是(num+1)/2。如果是偶数,就是叶子节点到叶子节点,如果是奇数,那么就是在奇数-1情况下的树下加一条叶子到其祖先的链。

偶数的情况:从一个非叶子节点出发,如果其子节点的叶子节点是偶数,则ans+=2,如果是奇数,ans+=1。

奇数的情况:枚举一下那条单链所在的子树。

设dp[u][i][j] 在u的子树中,u的父边需要经过i次,j表示单链是否在该子树中。j=1就额外枚举单链所在子树即可,如果单链在该子树中,那么该树中的有效叶子节点-1(即奇数变偶数,偶数变奇数)。

树形dp的难点就在于子树和父节点的关系

#include <bits/stdc++.h>using namespace std;const int inf = 0x3f3f3f3f;const int maxn=1e5+50;int t;int n;int dp[maxn][2];int eadg[maxn],son[maxn];vector<int>v[maxn];void dfs(int f,int x){    dp[x][0]=0;    dp[x][1]=inf;    int chd=0;    for(int i=0;i<v[x].size();i++)//先考虑x节点子节点各自走的步数    {        if(v[x][i]==f)            continue;        dfs(x,v[x][i]);        chd++;        son[x]+=son[v[x][i]];        int d=(son[v[x][i]]&1?1:2);//考虑x节点走到上一个节点步数,奇数+1,偶数+2        dp[x][0]+=dp[v[x][i]][0]+d;    }    for(int i=0;i<v[x].size();i++)//考虑x节点的叶子节点两两搭配之后的步数    {        if(v[x][i]==f)            continue;        if(son[v[x][i]]==1)            dp[x][1]=min(dp[x][0],dp[x][1]);        int d=(son[v[x][i]]&1?1:-1);        dp[x][1]=min(dp[x][1],dp[x][0]-dp[v[x][i]][0]+dp[v[x][i]][1]+d);    }    if(!chd) son[x]=1;}int main(){    cin>>t;    while(t--)    {        memset(son,0,sizeof(son));        memset(eadg,0,sizeof(eadg));        scanf("%d",&n);        for(int i=0;i<=n;i++)            v[i].clear();        int a,b;        for(int i=0;i<n-1;i++)        {            scanf("%d%d",&a,&b);            v[a].push_back(b);            v[b].push_back(a);            eadg[a]++;            eadg[b]++;        }        if(n==2)        {            puts("1");            continue;        }        int root;        int cnt=0;        for(int i=1;i<=n;i++)            if(eadg[i]!=1)                root=i;            else                cnt++;        dfs(0,root);        printf("%d\n",dp[root][cnt&1]);    }    return 0;}

0 0