hdoj3586 (树形dp)

题目链接:https://vjudge.net/problem/HDU-3586

题意:一棵边权树,要删掉一些边使得每个叶子结点不能到达树根,且这些边的权值<=上限Max,且边权和小于m,求最小的上限ans,如果不满足则输出-1。

思路:

  属于删边的树形dp。二分枚举上限Max,查找最小的满足要求的Max,用dp[u]表示结点u的子树中叶子结点不能到u所需删除的边的最小权值和,对于结点u,其子结点v,u->v的权值为w。那么:

  如果w>Max: dp[u]+=dp[v]

  如果w<=Max: dp[u]+=min(w,dp[v])

  对叶子结点u,dp[u]初始化为inf,对于非叶子结点u,dp[u]初始化为0,inf设为m+1,计算dp[u]过程只要dp[u]>=inf,即可提前退出。对每次Max的检测,只用判断dp[1]<=m即可。

AC code:

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn=1005;
int n,m,cnt,head[maxn],inf,Max,dp[maxn];

struct node{
    int v,w,nex;
}edge[maxn<<1];

void adde(int u,int v,int w){
    edge[++cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].nex=head[u];
    head[u]=cnt;
}

void dfs(int u,int fa){
    dp[u]=0;
    int flag=0;
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v,w=edge[i].w;
        if(v==fa) continue;
        flag=1;
        dfs(v,u);
        if(w>Max)
            dp[u]+=dp[v];
        else
            dp[u]+=min(w,dp[v]);
        if(dp[u]>=inf){
            dp[u]=inf;
            break;
        }
    }
    if(!flag)
        dp[u]=inf;
}

bool solve(int x){
    Max=x;
    dfs(1,0);
    return dp[1]<=m;
}

int main(){
    while(scanf("%d%d",&n,&m),n||m){
        cnt=0,inf=m+1;
        for(int i=1;i<=n;++i)
            head[i]=0;
        for(int i=1;i<n;++i){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            adde(u,v,w);
            adde(v,u,w);
        }
        int l=1,r=1000,mid;
        while(l<=r){
            mid=(l+r)>>1;
            if(solve(mid)) r=mid-1;
            else l=mid+1;
        }
        if(l==1001) printf("-1
");
        else printf("%d
",l);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/FrankChen831X/p/11469085.html