大逃杀 树形dp

本文详细介绍了一道关于树形动态规划的问题,通过增加状态维度优化原始的DP方案,最终实现高效求解最大得分路径。

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

   题目大意:给你一棵树,每条边有一个消耗值,每个点有一个点值得分,但有可能点会有一个消耗值,要拿到点值则必须先要克服这个点消耗值,然后给你一个有限消耗值,问在把这个消耗值耗完时最多能得到多少得分。n<=300.

  输入  

 17 54 
5 5 1 1 1 25 1 10 15 3 6 6 66 4 4 4 4 
0 1 3 0 0 0 1 3 2 0 6 7 54 0 0 0 0 
1 8 3 
2 8 3 
8 7 7 
7 13 0 
7 14 0 
15 14 2 
16 14 3 
17 14 5 
7 9 4 
9 10 25 
10 11 0 
10 12 0 
7 6 20 
3 6 3 
3 4 3 
3 5 3

输出

 68

分析:看一眼应该只有树形dp了,这不会有问题吧,然后不难想到转移方程f[i][j],表示以i为根时,给j个消耗值,所获的的最大得分。然后暴力的写了一发,n4方,结果调不过样例,gg。那说明f[i][j]肯定是有缺陷的,于是考虑加一维,f[i][j][2],0表示从i出发回到i,1表示从i出发不回到i,2表示从i的一个儿子出发,先到i再出去。好像挺抽象的,那我献丑画个图

好像很科学的样子,没错,就是很科学,现在考虑转移。

f[i][j][2]可以是f[u][i-2*w-j][2]+f[v][j][0],相当于选了f[u][i-2*w-j][2],在选他子树中的东西,子树要回来,就是图三上那些吊着的坨坨,因为要来回所以减边两次边权值;f[u][i-2*E[e].w-j][0]+f[v][j][2],在他儿子中进来出去,相当于图三中已经选完了那些吊着的坨坨,现在要出去了,因为要来回所以减边两次;f[u][i-E[e].w-j][1]+f[v][j][1],从他儿子中进来,在从自己出去;因为从儿子进来就出去了,所以只减一次。(坨坨就相当于i的已经处理的子树,直接拿就好了,相当于一个个背包。

然后1,0,的情况同理,自己分析一下就好了,还要简单些。

f[i][j][1]=f[u][i-2*E[e].w-j][1]+f[v][j][0],f[u][i-E[e].w-j][0]+f[v][j][1];

f[i][j][0]=f[u][i-2*E[e].w-j][0]+f[v][j][0];

如果对树形dp不太熟建议先去看一看树上背包,应该对这题的理解有帮助。

# include <iostream>
# include <cstdio>
# include <cstring>
# include <cmath>
# include <list>
# include <map>
# include <queue>
# include <algorithm>
using namespace std;
typedef long long ll;
int read()
{
	int f=1,i=0;char ch=getchar();
	while(ch<'0'||ch>'9') 
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		i=(i<<3)+(i<<1)+ch-'0';
		ch=getchar();
	}
	return f*i;
}
struct node
{
	int v,w,next;
}E[305<<1];
int n,k,x,y,z,cnt;
int tmp[305],f[305][305][3],t[305],w[305],first[305];
bool vis[305];
inline void AddEdge(int x,int y,int z)
{
	E[++cnt].next=first[x];
	first[x]=cnt;
	E[cnt].v=y;E[cnt].w=z;
}
inline void DFS(int u)
{
	vis[u]=true;
	for(int i=t[u];i<=k;++i) f[u][i][0]=f[u][i][1]=f[u][i][2]=w[u];
	for(int e=first[u];e;e=E[e].next)
	{
		int v=E[e].v;
		if(!vis[v])
		{
			DFS(v); 
			for(int i=0;i<=k;++i) tmp[i]=f[u][i][2];
			for(int j=0;j<=k;++j)
			{
				for(int i=2*E[e].w+t[u]+j;i<=k;++i)
				    tmp[i]=max(tmp[i],f[u][i-2*E[e].w-j][2]+f[v][j][0]);
				
				for(int i=2*E[e].w+t[u]+j;i<=k;++i)
				    tmp[i]=max(tmp[i],f[u][i-2*E[e].w-j][0]+f[v][j][2]);
				
				for(int i=E[e].w+t[u]+j;i<=k;++i)
				    tmp[i]=max(tmp[i],f[u][i-E[e].w-j][1]+f[v][j][1]);
			}
			for(int i=0;i<=k;++i) f[u][i][2]=tmp[i];//两边都不回头
			
			for(int i=0;i<=k;++i) tmp[i]=f[u][i][1];
			for(int j=0;j<=k;++j)
			{
				for(int i=2*E[e].w+t[u]+j;i<=k;++i)
				    tmp[i]=max(tmp[i],f[u][i-2*E[e].w-j][1]+f[v][j][0]);
				
				for(int i=E[e].w+t[u]+j;i<=k;++i)
				    tmp[i]=max(tmp[i],f[u][i-E[e].w-j][0]+f[v][j][1]);
			}
			for(int i=0;i<=k;++i) f[u][i][1]=tmp[i];//一边回头一边不回 
			
			for(int i=0;i<=k;++i) tmp[i]=f[u][i][0];
			for(int j=0;j<=k;++j)
			{
				for(int i=2*E[e].w+t[u]+j;i<=k;++i)
				    tmp[i]=max(tmp[i],f[u][i-2*E[e].w-j][0]+f[v][j][0]);
			}
			for(int i=0;i<=k;++i) f[u][i][0]=tmp[i];//都回头 
		}
	}
}
int main()
{
	
	n=read();k=read();
	for(int i=1;i<=n;++i) 
	    w[i]=read();
	for(int i=1;i<=n;++i) 
	    t[i]=read();
	for(int i=1;i<n;++i)
	{
		x=read(),y=read(),z=read();
		AddEdge(x,y,z),AddEdge(y,x,z);
	}
	DFS(1);
	int ans=0;
	for(int i=1;i<=n;++i)
	    for(int j=0;j<=k;++j)
	        for(int l=0;l<3;++l) ans=max(ans,f[i][j][l]);
	cout<<ans<<endl;
	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值