http://acm.hdu.edu.cn/showproblem.php?pid=3586
5 5 1 3 2 1 4 3 3 5 5 4 2 6 0 0
3
/**
hdu 3586 树形dp二分求解
题目大意:给定一棵树n个点,点1是根节点,每个叶子节点都向根节点传递“消息”,我们要在一些路上阻止,使路径不通。每条路上的阻止代价为该路权值,
我们能阻止的权值有一个上限x,而且所有叶子节点阻止的代价和不能超过m。问最小的可行x
解题思路:假设我们已经到了一个x,那么以u为根节点的子树的总花费dp[u],若边权值w<=x,dp[u]+=min(dp[v],w),否则dp[u]+=min(dp[v],inf),
这里的inf为任意一个大于m的数(但注意不要爆掉int,我取的m+1)。解释一下为什么用inf,因为w值已经超过限制我们不能阻止w边了,
那么要阻止以v为根节点子树的所有叶子节点,只能是dp[v]了。这样树形dp就可以求出dp[1],和m比较即可知道我们的x值合不合适了。
对于x二分就可以了。
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=1005;
struct note
{
int v,w,next;
}edge[maxn*2];
int head[maxn],ip;
int n,m,mid,dp[maxn];
void init()
{
memset(head,-1,sizeof(head));
ip=0;
}
void addedge(int u,int v,int w)
{
edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
}
void dfs(int u,int pre)
{
int flag=0;
for(int i=head[u];i!=-1;i=edge[i].next)///求解父亲节点前它的所有儿子节点必须全部已经求出
{
int v=edge[i].v;
if(v==pre)continue;
flag=1;
dfs(v,u);
}
if(flag==0)///叶子节点
{
dp[u]=m+1;///任意一个大于m的数
return;
}
int t=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int w=edge[i].w;
int v=edge[i].v;
if(v==pre)continue;
if(w<=mid)
dp[u]+=min(dp[v],w);
else
dp[u]+=min(dp[v],m+1);
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0)break;
int maxx=0;
init();
for(int i=0;i<n-1;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
maxx+=w;
}
int l=1,r=maxx;
int flag=0;
while(l<r)
{
mid=(l+r)>>1;
memset(dp,0,sizeof(dp));
dfs(1,-1);
// printf("%d %d\n",mid,dp[1]);
if(dp[1]<=m)
{
flag=1;
r=mid;
}
else
l=mid+1;
}
if(flag==0)
printf("-1\n");
else
printf("%d\n",r);
}
return 0;
}
/**
5 4
1 3 2
1 4 3
3 5 5
4 2 6
5 4
1 3 2
1 4 2
3 5 5
4 2 6
5 3
1 3 2
1 4 2
3 5 5
4 2 6
*/
原文:http://blog.csdn.net/lvshubao1314/article/details/43739657