L2-043 龙龙送外卖

首先这是一个树的结构,题目中也说了。每多出现一个点,需要重新规划从根节点开始到所有需要到达的点的最短距离,当然每个点的访问顺序可以是乱序的,只要保证都到达了即可。对于所有需要到达的点,其连到父节点的边一定是要经历的,也就是说假如有一条边一个端点为 u ,其父节点也就是这条边的另一条端点 v,那么如果 u是需要到达的节点之一,这条 u−>v 的边是一定需要经过的,其原因是这是一个树的结构,到达 u的路径一定只能经过其父节点到达。

根据这一点,而且从根节点到达子节点路径是唯一的,所以所有从根节点到某一子节点上路径涉及到的边一定都需要经历,且由于只能从根节点出发,所以有很多边需要经历两次。但问题在于,最短路径距离如何确定,有哪些边只需要经历一次,有哪些边需要经历两次。

不妨逆向思维考虑这个问题,题中说从根节点出发但是不必要回到根节点。我们可以先将所有涉及到的边都经历两次,算出此时的路径距离。由于我们可以不用返回根节点,而所有涉及的边都经历两次,必定有一条从根节点到某一子节点的路径是多余的。此时,我们可以采取贪心的策略,因为要求最短路径距离,所以应当选取最长的一条从根节点到当前需要到达的子节点的距离,将这个距离减去,那么最终得到的就是当前的最短路径距离。

所以最后,我们的问题转换为,累加所有涉及的边两倍的边权(边权为1,所以每次累加2),然后减去一条最长的从根节点到需要到达的子节点的距离,这个最长距离随着需要到达的点的增多而更新。

设 所有点的深度和为sum, 最后访问的d,我们假设需要返回外卖站,那么从源点到所有需要到达点的距离等于 2*sum, 那么不需要返回那, 那么 这个路径可以表示为,2*sum-d,因为最后访问的不需要返回,其他需要返回的,在该路径上加一个到达该路径上的回路就ok了。


#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, sum, mx;
int p[N], d[N];//记录父节点的数组和记录到相应节点长度的数组

int dfs(int u) {
	if (p[u] == -1 || d[u] > 0) return d[u];//根节点返回0而相应其它节点(>0说明已经求过值)直接返回
	sum ++;//递归一层即其相应加1
	d[u] = dfs(p[u]) + 1;//递归思想即其到本节点的值为相应到父节点的值加一
	return d[u];
}

int main() {
	int t;
	cin >> n >> m;
	for (int i = 1; i <= n; i ++) {
		cin >> p[i];
	}

	while (m --) {
		cin >> t;	
		mx = max(mx, dfs(t));	
		cout << sum * 2 - mx << endl;
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值