HDU5758(Explorer Bo 最小链覆盖的 所有链的长度总和最小 树上链dp)

题目
在这里插入图片描述题意: 给你1颗树。起点可以在n个点之间任意选择。每次走到头了,转方向或切换到另一个点都花费1个魔法。它想把n-1条道路都经过一遍,但是想花费魔法的次数最少。问最少情况下它走路最短是多少。
思路: 首先,一棵树的叶子节点的个数不管以谁为根都是一样的。假如这颗树的叶子节点个数是num。1.num是偶数 那么它使用的魔法次数最少就是num/2,叶子节点两两结合形成链。 2.num是奇数。使用的魔法次数最少就是(num+1)/2,其实就是其中num-1个叶子节点两两配对,其中一个直接连向根节点(花费为1)。
然后,x的儿子是y 假如y子树(包括y)里面呢叶子节点的个数是奇数,这个边的耗费就是1,因为配对好的叶子节点,她们之间的链只经过y,不经过x-y这条边。 假如是偶数,需要这些对里面其中有一对经过x-y这个边。(因为这个边要被覆盖。) 这就是第一个dfs的过程。多试几个不同的根dfs, 可以发现以谁为根都是一样的。
假如这棵树的叶子节点是偶数,那么dp[root]就是本题所要求的答案。
否则,这棵树需要枚举去除一个叶子节点的那个链,然后+1他的花费。
dfs2到叶子y结点的父亲x时候,先dfs2 y返回0 然后sz[y]=1 此时tmp=-1 但是return mx=0,这个时候代表是算上了那1个花费。 dfs2的撤销过程就是dfs的dp过程。
可是为什么对于任意一个点做根的答案一样呢!?我根本没试,感觉这题好懵逼啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。

#include<cstdio>
#include<iostream>
#include<cstring>
#define m(a,b) memset(a,b,sizeof a)
using namespace std;
const int N=1e5+5;
struct Edge{int to,nex;}edge[N<<1];int head[N],tot;
inline void add(int from,int to){
    edge[++tot]=(Edge){to,head[from]},head[from]=tot;
    edge[++tot]=(Edge){from,head[to]},head[to]=tot;
}
int sz[N],dp[N];
void dfs(int x,int fa){
    int flag=0;
    for(int i=head[x];i;i=edge[i].nex){
        int y=edge[i].to;
        if(y==fa) continue;
        flag=1,dfs(y,x),sz[x]+=sz[y];
        dp[x]+=dp[y]+((sz[y]&1)?1:2);
    }
    if(!flag) sz[x]=1;
}
int dfs2(int x,int fa){
    int mx=0;
    for(int i=head[x];i;i=edge[i].nex){
        int y=edge[i].to;
        if(y==fa) continue;
        int tmp=dfs2(y,x);
        tmp+=((sz[y]&1)?-1:1);
        mx=max(mx,tmp);
    }
    return mx;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        m(dp,0),m(sz,0),m(head,0),tot=0;
        int n;scanf("%d",&n);
        if(n==1) {puts("0");continue;}
        for(int i=1,x,y;i<n;++i)
            scanf("%d%d",&x,&y),add(x,y);
        if(n==2) {puts("1");continue;}
        int root;
        for(int i=1;i<=n;++i)
            if(edge[head[i]].nex) {root=i;break;}
        dfs(root,root);
        if(!(sz[root]&1)) printf("%d\n",dp[root]);
        else printf("%d\n",dp[root]-dfs2(root,root));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值