2020 Multi-University Training Contest 2 1007 In Search of Gold

题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1007&cid=880

题意:给你一个n个点,n-1条边的树,每条边的长度可能是a[i],也可能是b[i],有k条边的长度是取a[i],n-k-1条边的长度是取b[i]。然后要你求这棵树可能的最小直径。

思路:要求可能的最小直径,可以想到二分数的直径看是否满足。二分看是否满足时可以用树型DP, f[i][j] 表示在 i 的子树,其中有 j 条边的的长度是取 a 数组的所有树直径 ≤ mid 的方案中,与 i 点距离最远的点到 i 的距离的最小

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int g[20005],v[40005],a[40005],b[40005],nxt[40005],siz[20005];
ll f[20005][25],h[20005];
int n,m;
void dfs(int x,int y,ll mid)
{
    siz[x]=f[x][0]=0;
    for(int i=g[x]; i; i=nxt[i])
    {
        int u=v[i];
        if(u==y)
            continue;
        dfs(u,x,mid);
        int A=a[i],B=b[i],pre=siz[x],cur=siz[u];
        int now=min(pre+cur+1,m);
        for(int j=0; j<=now; j++)
            h[j]=mid+1;
        for(int j=0; j<=pre; j++)
            for(int k=0; k<=cur&&j+k<=m; k++)
            {
                if(f[x][j]+f[u][k]+A<=mid)
                    h[j+k+1]=min(h[j+k+1],max(f[x][j],f[u][k]+A));
                if(f[x][j]+f[u][k]+B<=mid)
                    h[j+k]=min(h[j+k],max(f[x][j],f[u][k]+B));
            }
        siz[x]=now;
        for(int j=0; j<=now; j++)
            f[x][j]=h[j];
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%d%d",&n,&m);
        ll l=0,r=0;
        for(int i=0; i<=n; i++)
            g[i]=0;
        int ed=0;
        for(int i=1; i<n; i++)
        {
            int x,y,A,B;
            scanf("%d%d%d%d",&x,&y,&A,&B);
            v[++ed]=y;
            a[ed]=A;
            b[ed]=B;
            nxt[ed]=g[x];
            g[x]=ed;
            v[++ed]=x;
            a[ed]=A;
            b[ed]=B;
            nxt[ed]=g[y];
            g[y]=ed;
            r+=max(A,B);
        }
        ll ans=0;
        while(l<=r)
        {
            ll mid=(l+r)/2;
            dfs(1,0,mid);
            if(f[1][m]<=mid)
            {
                ans=mid;
                r=mid-1;
            }
            else
                l=mid+1;
        }
        printf("%lld\n",ans);
    }
}

 

可能值。然后转移时合并之前已经 DP过的部分和新加入的子树,如果两部分到 i 的最长距离之和超过 mid 则这颗树的直径已经超过了mid,就不需要转移了。

上一篇:第四十二节 jQuery之bootstrap容器


下一篇:NDK中的循环依赖问题及使用gold解决