HDU 5758 Explorer Bo (树形DP)

75 篇文章 0 订阅

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

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

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

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

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


[code]:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>

using namespace std;
const int maxn = 2e5+5;

struct Nod{
    int b,next;
    void init(int b,int next){
        this->b=b;this->next=next;
    }
}buf[2*maxn];
int n,E[maxn],len,cnt[maxn],ans,deg[maxn];
int dp[maxn][3][2];

void init(){
    ans = len = 0;
    memset(E,-1,sizeof(E));
    memset(dp,-1,sizeof(dp));
    memset(deg,0,sizeof(deg));
}
void add_edge(int a,int b){
    buf[len].init(b,E[a]);E[a]=len++;
    buf[len].init(a,E[b]);E[b]=len++;
    deg[a]++;deg[b]++;
}

void pre_dfs(int u,int pre){
    int i,v,f = 0;
    cnt[u] = 0;
    for(i = E[u];i != -1;i = buf[i].next){
        v = buf[i].b;
        if(v == pre) continue;
        pre_dfs(v,u);
        cnt[u]+=cnt[v];
        f = 1;
    }
    if(!f) cnt[u] = 1;
}
int dfs(int u,int pre,int s1,int s2){
    if(dp[u][s1][s2]!=-1) return dp[u][s1][s2];
    int i,v,ans = 0,num = -s1,tmp;
    for(i = E[u];i != -1;i = buf[i].next){
        v = buf[i].b;
        if(v == pre) continue;
        num += cnt[v];
        if(cnt[v]&1) ans += dfs(v,u,1,0)+1;
        else ans += dfs(v,u,2,0)+2;
    }
    if(s2){
        tmp = 0x3f3f3f3f;
        for(i = E[u];i != -1;i = buf[i].next){
            v = buf[i].b;
            if(v == pre) continue;
            if(cnt[v]&1){
                if(cnt[v]==1) tmp = min(tmp,ans);
                else tmp = min(tmp,ans+dfs(v,u,2,1)+2-(dfs(v,u,1,0)+1));
            }else tmp = min(tmp,ans+dfs(v,u,1,1)+1-(dfs(v,u,2,0)+2));
        }
        ans = tmp;
    }
    return dp[u][s1][s2] = ans;
}

int main(){
    int i,j,cas,u,v;
    scanf("%d",&cas);
    while(cas--){
        scanf("%d",&n);
        init();
        for(i = 1;i < n;i++){
            scanf("%d%d",&u,&v);
            add_edge(u,v);
        }
        if(n == 2){
            puts("1");
            continue;
        }
        int root=-1,num = 0;
        for(i = 1;i <= n;i++){
            if(deg[i]!=1&&root==-1){
                root = i;
            }
            num += deg[i]==1;
        }
        pre_dfs(root,-1);
        printf("%d\n",dfs(root,-1,0,num&1));
    }

    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值