hdu4679 Terrorist’s destroy(树形dp)

hdu4679

题目

就是给你一棵树,每条边有边权值,去掉这条边花费他的边权代价,然后树会分成两个子树,求两个个子树的最长链的最大值和去掉边的代价的乘积的最小值,输出那条边。

思路

做了一些树型dp,还是感觉学习了是什么鬼。。。
http://blog.csdn.net/yang_7_46/article/details/9988655 这位博主讲得很好了。
首先是学习了找树的最长链,两遍dfs,然后预处理每个点及其子树到两端点的最长距离,然后分情况考虑删去的边在不在树的最长链上。

代码

#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<stack>

using namespace std;

typedef  long long ll;

const int maxn=100100;

int head[maxn],tot,n,id,len,ans;
int ds[maxn],dt[maxn];
int mt[maxn],ms[maxn];

struct node
{
    int next;
    int to;
    int w;
    int id;
} edge[maxn*2];

void addedge(int from,int to,int w,int id)
{
    edge[tot].to=to;
    edge[tot].next=head[from];
    edge[tot].w=w;
    edge[tot].id=id;
    head[from]=tot++;
}

void dfs1(int u,int fa)
{
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v=edge[i].to;
        if(fa==v) continue;
        ds[v]=ds[u]+1;
        dfs1(v,u);
    }
}

void dfs2(int u,int fa)
{
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v=edge[i].to;
        if(fa==v) continue;
        dt[v]=dt[u]+1;
        dfs2(v,u);
    }
}


void d1(int u,int fa)
{
    ms[u]=ds[u];
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        d1(v,u);
        ms[u]=max(ms[u],ms[v]);
    }
}


void d2(int u,int fa)
{
    mt[u]=dt[u];
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        d2(v,u);
        mt[u]=max(mt[u],mt[v]);
    }
}

void solve(int u,int fa)
{
    int w;
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        if(ds[u]+1+dt[v]==len)
            w=edge[i].w*max(ms[u],mt[v]);
        else w=edge[i].w*len;

        if(w<ans)
        {
            ans=w;
            id=edge[i].id;
        }
        else if(w==ans&&edge[i].id<id)
            id=edge[i].id;
        solve(v,u);

    }
}

int main()
{
    int kase=1;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ans=0x3f3f3f3f;
        tot=0;
        memset(head,-1,sizeof(head));
        scanf("%d",&n);
        for(int i=1; i<=n-1; i++)
        {
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            addedge(a,b,c,i);
            addedge(b,a,c,i);
        }
        ds[1]=ds[0]=0;
        dfs1(1,0);
        int s=0;
        for(int i=1; i<=n; i++) if(ds[i]>ds[s]) s=i;
        ds[s]=0;
        dfs1(s,0);
        int t=0;
        for(int i=1; i<=n; i++) if(ds[i]>ds[t]) t=i;
        len=ds[t];

        dt[t]=0;
        dfs2(t,0);
        d1(t,0);
        d2(s,0);
        solve(s,0);
        printf("Case #%d: %d\n", kase++, id);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值