洛谷 P3647 [APIO2014]连珠线【树形dp】【换根】

这篇博客探讨了一种图的构造问题,其中涉及两种操作:红绳连接和蓝绳插入。目标是找到最大化蓝绳数量的策略,使最终形成一棵树。博主分析了如何通过动态规划和换根策略求解此问题,并提供了相应的C++代码实现。文章深入浅出地解释了算法思路,包括状态转移方程和递归过程。

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


题目:

传送门


题意:

我们有两种操作
1. ( u , v ) 1.(u,v) 1.(u,v)视为红绳
2. ( u , v , k ) 2.(u,v,k) 2.(u,v,k)表示将 k k k插入到 ( u , v ) ( (u,v)( (u,v)(一定要是红绳 ) ) )中间, ( u , k ) 、 ( k , v ) (u,k)、(k,v) (u,k)(k,v)视为蓝绳,并计入到答案
我们现在有 n n n个端点,知道最后哪两个端点间有连线,并最后呈现一棵树的形态
问我们如何安排操作,能使得答案最大,求这个最大答案是多少


分析:

挺考思维的吧(也可能是我菜
因为我们原本是无根的无环连通的无向图,我们不妨随便找个点定为根,看下此时的答案会是怎么样的
在此之前,通过题意,我们可以了解到一个端点,最多只能是两根蓝绳的交界点,也就是要是不是蓝绳的中间点,要么是,也只能充当一次中间点
如此一来设 f u , 0 f_{u,0} fu,0表示在 u u u点处, u u u不是中间点的最大答案, f u , 1 f_{u,1} fu,1表示是中间点的
f u , 0 = ∑ v   ϵ   u s o n m a x { f v , 0 , f v , 1 + w } f_{u,0}=\sum_{v\ \epsilon\ u_{son}} max\{f_{v,0},f_{v,1}+w\} fu,0=v ϵ usonmax{fv,0,fv,1+w}
f u , 1 f_{u,1} fu,1则是将连向某一个子节点的线变成蓝绳,而其他的跟红绳一样计算
f u , 1 = f u , 0 + m a x { f v , 0 + w − m a x { f v , 0 , f v , 1 + w } } f_{u,1}=f_{u,0}+max\{f_{v,0}+w-max\{f_{v,0},f_{v,1}+w\}\} fu,1=fu,0+max{fv,0+wmax{fv,0,fv,1+w}}
后面的那个减法表示减去该子节点作为红绳的贡献
现在已经考虑好确定跟的情况了,现在问题来到如何求出换根后的答案
换根 ( u (u (u变为 v ) v) v),对原本答案的影响有:
1. v 1.v 1.v的贡献消失了
2. u 2.u 2.u变成了 v v v的子节点,我们需要计算此时 u u u对答案的贡献
对于 f x , 0 f_{x,0} fx,0,我们直接减去贡献就好了
对于 f x , 1 f_{x,1} fx,1,我们需要保存最大值和次大值,当 v v v作为最大贡献的蓝绳的一个端点时,这条蓝绳不一定能存在了,所以需要变为次大值
u u u v v v都是这样更新,不要忘了在遍历完 v v v的子树后,对 u u u进行回溯,再遍历 u u u的下一个子节点


代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
inline LL read()
{
	LL s=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {s=s*10+c-'0';c=getchar();}
	return s*f;
}
struct node{
	int to,next,w;
}e[400005];
int ls[200005],cnt=0;
void add(int x,int y,int w)
{
	e[cnt]=(node){y,ls[x],w};
	ls[x]=cnt++;
	return;
}
int f[200005][2];
int m1[200005],m2[200005];
void dfs(int u,int fa)
{
	for(int i=ls[u];~i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		f[u][0]+=max(f[v][0],f[v][1]+e[i].w);
	}
	f[u][1]=f[u][0];
	m1[u]=m2[u]=-2147483647;
	for(int i=ls[u];~i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==fa) continue;
		int bc=f[v][0]+e[i].w-max(f[v][0],f[v][1]+e[i].w);
		if(bc>m1[u]) m2[u]=m1[u],m1[u]=bc;
		else if(bc>m2[u]) m2[u]=bc;
	}
	f[u][1]+=m1[u];
	return;
}
int ans=-2147483647;
void change(int u,int fa)
{
	ans=max(ans,f[u][0]);
	int b0=f[u][0],b1=f[u][1],b2=m1[u],b3=m2[u];
	for(int i=ls[u];~i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==fa) continue;
		f[u][0]-=max(f[v][0],f[v][1]+e[i].w);
		f[u][1]-=max(f[v][0],f[v][1]+e[i].w);
		int b=f[v][0]+e[i].w-max(f[v][0],f[v][1]+e[i].w);
		if(b==m1[u]) f[u][1]+=m2[u]-m1[u];
		f[v][0]+=max(f[u][0],f[u][1]+e[i].w);
		f[v][1]+=max(f[u][0],f[u][1]+e[i].w);
		f[v][1]-=m1[v];
		b=f[u][0]+e[i].w-max(f[u][0],f[u][1]+e[i].w);
		if(b>m1[v]) m2[v]=m1[v],m1[v]=b;
		else if(b>m2[v]) m2[v]=b;
		f[v][1]+=m1[v];
		change(v,u);
		f[u][0]=b0;f[u][1]=b1;
		m1[u]=b2;m2[u]=b3;
	}
	return;
}
int main()
{
	memset(ls,-1,sizeof(ls));
	int n=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read(),w=read();
		add(x,y,w);add(y,x,w);
	}
	dfs(1,0);
	change(1,0);
	cout<<ans;
	return 0;
}
飞思卡尔智能车竞赛是一项备受关注的科技赛事,旨在激发学生的创新和实践能力,尤其是在嵌入式系统、自动控制和机器人技术等关键领域。其中的“电磁组”要求参赛队伍设计并搭建一辆能够自主导航的智能车,通过电磁感应线圈感知赛道路径。本压缩包文件提供了一套完整的电磁组智能车程序,这是一套经过实战验证的代码,曾在校级比赛中获得第二名的优异成绩。 该程序的核心内容可能涉及以下关键知识点: 传感器处理:文件名“4sensor”表明车辆配备了四个传感器,用于获取环境信息。这些传感器很可能是电磁感应传感器,用于探测赛道上的导电线圈。通过分析传感器信号的变化,车辆能够判断自身的行驶方向和位置。 数据采集与滤波:在实际运行中,传感器读数可能受到噪声干扰,因此需要进行数据滤波以提高精度。常见的滤波算法包括低通滤波、高斯滤波和滑动平均滤波等,以确保车辆对赛道的判断准确无误。 路径规划:车辆需要据传感器输入实时规划行驶路径。这可能涉及PID(比例-积分-微分)控制、模糊逻辑控制或其他现代控制理论方法,从而确保车辆能够稳定且快速地沿赛道行驶。 电机控制:智能车的驱动通常依赖于直流电机或无刷电机,电机控制是关键环节。程序中可能包含电机速度和方向的调节算法,如PWM(脉宽调制)控制,以实现精准的运动控制。 嵌入式系统编程:飞思卡尔智能车的控制器可能基于飞思卡尔微处理器(例如MC9S12系列)。编程语言通常为C或C++,需要掌握微控制器的中断系统、定时器和串行通信等功能。 软件架构:智能车软件通常具有清晰的架构,包括任务调度、中断服务程序和主循环等。理解和优化这一架构对于提升整体性能至关重要。 调试与优化:程序能够在比赛中取得好成绩,说明经过了反复的调试和优化。这可能涉及代码效率提升、故障排查以及性能瓶颈的识别和解决。 团队协作与版本控制:在项目开发过程中,团队协作和版本控制工具(如Git)的应用不可或缺,能够保
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值