UVALive - 4015(树形DP)

这篇博客介绍了如何使用树形动态规划解决一个关于有能量限制的树形路径问题。给定一棵带有权值的树和一个能量有限的机器人,目标是确定机器人在消耗x点能量下能访问的最大节点数。博主通过定义状态和转移方程,并结合0-1背包的思想,提供了求解此问题的方法,同时提到由于节点数量和查询次数的限制,可以采用二分搜索等暴力查询策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你一颗带有权值的树,接着一个有能量的机器人,每经过一个边就要消耗对应权值的能量,求你x点能量最大能经过你几个结点

题解:我们可以dp出我 走i个点消耗最小的能量,因为我只有两种情况要么从另外一颗子树经过 j个结点然后走回来或者从另外一颗子树走j个结点不会来,所以我们要记录一些该节点走j个结点回来的最小值和不回来的最小值,接着可得转移方程就是

dp[u][i].fi = min(dp[u][i-j].fi+dp[v][j].se+2*w,min(dp[u][i].fi,dp[v][j].fi+dp[u][i-j].se+w));
dp[u][i].se = min(dp[u][i].se,dp[v][j].se+dp[u][i-j].se+2*w);

fi表示不走回来使用最少能量,se表示走回来使用最小能量,接着我们可以用0-1背包的思想先把i从后往前更新确保前面的更新不会影响后面的更新接着最后因为结点最大只有500,查询只有1W,二分暴力查询都可以

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#include<cstdlib>
#include<ctime>
#include<stack>
using namespace std;
#define mes(a) memset(a,0,sizeof(a))
#define rep(i,a,b) for(i = a; i <= b; i++)
#define dec(i,a,b) for(i = b; i >= a; i--)
#define fi first
#define se second
#define ls rt<<1
#define rs rt<<1|1
#define mid (L+R)/2
#define lson ls,L,mid
#define rson rs,mid+1,R
typedef double db;
typedef long long int ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
const int mx = 1e5+5;
const int inf = 0x3f3f3f3f;
const int x_move[] = {1,-1,0,0,1,1,-1,-1};
const int y_move[] = {0,0,1,-1,1,-1,1,-1};
int n,m;
vector<pii>g[mx];
pii dp[505][505];
void dfs(int u,int fa){
	for(auto it: g[u]){
		int v = it.fi;
		int w = it.se;
		if(v!=fa){
			dfs(v,u);
			for(int i = n; i >= 2; i--)
				for(int j = 1; j < i; j++){
					dp[u][i].fi = min(dp[u][i-j].fi+dp[v][j].se+2*w,min(dp[u][i].fi,dp[v][j].fi+dp[u][i-j].se+w));
					dp[u][i].se = min(dp[u][i].se,dp[v][j].se+dp[u][i-j].se+2*w);
				}
		}
	}
}
int main(){
	int t,q,ca = 1;
	while(scanf("%d",&n)&&n){
		memset(dp,inf,sizeof(dp));
		for(int i = 0; i < n; i++){
			g[i].clear();
			dp[i][1].fi = 0;
			dp[i][1].se = 0;
		}
		for(int i = 2; i <= n; i++){
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			g[u].push_back(make_pair(v,w));
			g[v].push_back(make_pair(u,w));
		}
		dfs(0,0);
		scanf("%d",&q);
		printf("Case %d:\n",ca++);
		while(q--){
			int x;
			scanf("%d",&x);
			int ans = 1;
			for(int i = 1; i <= n; i++){
				if(x>=dp[0][i].fi)	ans = i;
				//cout<<dp[0][i].fi<<endl;
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值