【XSY3331】东非大裂谷(结论,DP)

博客介绍了如何利用树形动态规划解决序列中分段求极值和的问题,强调了两个关键结论:段内的最大值和最小值必定位于两端,且一段必须是单调的。通过设置两个状态分别表示子树内单调不增和单调不降的最优解,最终在根节点获取全局最大值。

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

一般这种 “分段,求每段极值和的最大值” 的题都有两个结论:

  1. 一段的最大值和最小值一定是该段的两个端点。

    证明:如果不是的话:那么我们显然可以把最小值和最大值所在位置之间的部分提取出来作为一段,而其他的部分分离出去,这样得到的答案肯定不劣。

  2. 一段肯定是单调不增或单调不降的。

    证明:如果不是的话:假设该段为 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an。由结论1,我们可以假设 a 1 a_1 a1 该段的最小值、 a n a_n an 为该段的最大值( a 1 a_1 a1 为最大、 a n a_n an 为最小同理)。然后若此段不是单调不降的,那么我们找到某一个 a m i d > a m i d + 1 a_{mid}>a_{mid+1} amid>amid+1 的位置,然后把该段划分为新的两段 a 1 , ⋯   , a m i d a_1,\cdots,a_{mid} a1,,amid a m i d + 1 , ⋯   , a n a_{mid+1},\cdots,a_n amid+1,,an,这样得到的答案肯定是更优的。

那么这题相当于把序列上的问题转移到树上,那么直接设 d p u , 0 / 1 dp_{u,0/1} dpu,0/1 表示只考虑 u u u 子树内,其中 u u u 所在段是单调不增/单调不升的最优答案。

#include<bits/stdc++.h>

#define N 100010
#define ll long long

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,w[N];
int cnt,head[N],nxt[N],to[N];
ll dp[N][2];//0 上升 1 下降 

void adde(int u,int v)
{
	to[++cnt]=v;
	nxt[cnt]=head[u];
	head[u]=cnt;
}

void dfs(int u)
{
	ll sum=0;
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		dfs(v);
		sum+=max(dp[v][0],dp[v][1]);
	}
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(w[v]>=w[u]) dp[u][0]=max(dp[u][0],sum-max(dp[v][0],dp[v][1])+w[v]-w[u]+dp[v][0]);
		if(w[v]<=w[u]) dp[u][1]=max(dp[u][1],sum-max(dp[v][0],dp[v][1])+w[u]-w[v]+dp[v][1]);
	}
	dp[u][0]=max(dp[u][0],sum);
	dp[u][1]=max(dp[u][1],sum);
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		adde(u,v);
	}
	dfs(1);
	printf("%lld\n",max(dp[1][0],dp[1][1]));
	return 0;
}
/*
7
5 5 3 6 2 3 3
1 6
5 3
1 5
6 2
2 4
6 7
*/
/*
4
2 3 4 1
1 2
2 3
3 4
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值