hdu 3586树型DP

依旧。。不会。。

二分,因为题中说要找到代价和小于m并且输出那个代价和中最大的边,就要用到二分了。对每个边判断其实否小于二分中mid的值(也就是limit)

状态转移方程:if(w[j与其孩子的边的代价]<=limit)dp[j]+=min(dp[j的孩子],w[连接j与其孩子的边的代价])

                            else dp[j]+=dp[j的孩子];

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define Max 2000
using namespace std;
int n,m,dp[2020],pos,list[1010];
struct P{
	int u,v,next,w;
}point[2020];
void add(int fa,int son,int wi){
	point[pos].u=fa;
	point[pos].v=son;
	point[pos].w=wi;
	point[pos].next=list[fa];
	list[fa]=pos++;
}
void dfs(int son,int fa,int limit){
	bool flag=false;
	int now=list[son];
	dp[son]=0;
	while(now!=-1){
		if(point[now].v==fa){
			now=point[now].next;
			continue;
		}
		dfs(point[now].v,son,limit);
		flag=true;
		if(point[now].w<=limit){
			dp[son]+=min(dp[point[now].v],point[now].w);
		}
		else{
			dp[son]+=dp[point[now].v];
		}
		now=point[now].next;
	}
	if(flag==0){
		dp[son]=Max;
	}
	return ;
}
int main(){
	while(scanf("%d%d",&n,&m)&&(n&&m)){
		pos=0;
		memset(list,-1,sizeof(list));
		int l=1,r=0,mid;
		for(int i=1;i<n;i++){
			int a,b,w;
			scanf("%d%d%d",&a,&b,&w);
			add(a,b,w);
			add(b,a,w);
			if(r<w)r=w;
		}
//		printf("! l=%d r=%d\n",l,r);
		int ans=Max;
		while(l<=r){
			mid=(r+l)/2;
			dfs(1,-1,mid);
			if(dp[1]<=m){
				r=mid-1;
				ans=mid;
			}
			else{
				l=mid+1;
			}
//		printf("! l=%d r=%d dp[1]=%d\n",l,r,dp[1]);
		}
		if(ans!=Max){
			printf("%d\n",ans);
		}
		else{
			printf("-1\n");
		}
	}
	return 0;
}



一开始二分判断为l<r 

但是wa了,后来发现,这个条件会少判断一次l==r 所以加一次就好了

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define Max 1000010
using namespace std;
int n,m,dp[2020],pos,list[1010];
struct P{
	int u,v,next,w;
}point[2020];
void add(int fa,int son,int wi){
	point[pos].u=fa;
	point[pos].v=son;
	point[pos].w=wi;
	point[pos].next=list[fa];
	list[fa]=pos++;
}
void dfs(int son,int fa,int limit){
	bool flag=false;
	int now=list[son];
	dp[son]=0;
	while(now!=-1){
		if(point[now].v==fa){
			now=point[now].next;
			continue;
		}
		dfs(point[now].v,son,limit);
		flag=true;
		if(point[now].w<=limit){
			dp[son]+=min(dp[point[now].v],point[now].w);
		}
		else{
			dp[son]+=dp[point[now].v];
		}
		now=point[now].next;
	}
	if(flag==0){
		dp[son]=Max;
	}
	return ;
}
int main(){
	while(scanf("%d%d",&n,&m)&&(n&&m)){
		pos=0;
		memset(list,-1,sizeof(list));
		int l=1,r=0,mid,ans=Max;
		for(int i=1;i<n;i++){
			int a,b,w;
			scanf("%d%d%d",&a,&b,&w);
			add(a,b,w);
			add(b,a,w);if(w>r)r=w;
		}
		printf("! l=%d r=%d\n",l,r);
		while(l<r){
			mid=(r+l)/2;
			dfs(1,-1,mid);
			if(dp[1]<=m){
				r=mid;ans=mid;
			}
			else{
				l=mid+1;
			}
			        printf("! l=%d r=%d dp[1]=%d\n",l,r,dp[1]);
		}
		dfs(1,-1,(r+l)/2);
		if(dp[1]<=m)ans=(r+l)/2;
		if(ans!=Max){
			printf("%d\n",ans);
		}
		else{
			printf("-1\n");
		}
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值