蓝桥杯2013年第四届真题-大臣的旅费

题目

题目链接

题解

本质上是一个计算树的直径的问题。(模板题)


树的直径有两种计算方式:

  1. 两次dfs。以任意一点X为根节点,第一次dfs找到距离X最远的节点P,第二次dfs找到距离节点P最远的节点QPQ的距离就是树的直径。
  2. 树型DP。两个数组mx[i]_mx[i]分别记录当前节点i到根节点(随意选取)的最大距离和次最大距离。假设ji的子结点,更新方式:如果mx[i] <= mx[j] + cost[i][j],则_mx[i] = mx[i]mx[i] = mx[j] + cost[i][j];否则,如果_mx[i] < mx[j] + cost[i][j],则_mx[i] = mx[j] + cost[i][j]

邻接表
模板代码

代码

两次dfs

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5+10;
int idx, n, u, v, w;
int h[N], ne[N<<2], e[N<<2];
int cost[N<<2]; // cost[i] 表示第i条边的权重 
int d[N]; // d[i]表示从节点i到根节点的距离(至于哪个是根节点,不同情况不一样) 
int p, ans; // p表示暂时找到中转点,ans当前搜索情况下的最长距离 

void add (int a, int b, int w) { // 邻接表添加边 
	cost[idx] = w, e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
} 

void dfs (int x, int fa) {
	if (ans < d[x]) { // x节点到根节点的距离比最大距离大,则更新最大距离 
		ans = d[x];
		p = x; // 记录距离根节点最远的节点 
	}
	
	for (int i = h[x];~i;i = ne[i]) { // 注意 ~ 
		int j = e[i]; // 根据边的编号得到该边对应的终点的编号 
		if (j == fa) continue; // 如果回头了,则continue 
		d[j] = d[x] + cost[i]; // 更新子结点 j 到根节点的距离(因为路径唯一,所以可以这样更新) 
		dfs (j, x); // 递归遍历 j 节点的子结点 
	}
}

void find (int x) { 
	ans = 0; // 初始化 ans 为 0 
	d[x] = 0; // 根节点到根节点的距离记为 0  
	dfs (x, 0); 
}

int main()
{
	memset (h, -1, sizeof h); // 初始化 
	cin >> n;
	for (int i = 1;i < n;i ++) {
		cin >> u >> v >> w;
		add (u, v, w);
		add (v, u, w);
	} 

	find (1); // 随便选一个点作为根节点,找距离其最远的节点 
	find (p); // 将上面找到的 p 作为根节点,找距离 p 最远的节点,该距离为树的直径 
	
	cout << ans * 10 + ans * (ans + 1) / 2 << endl; // 公式输出 

	return 0;
}

树型 DP

// 计算树的直径,树型DP 
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;

int n, m, u, v, w, idx;
int e[N<<2], ne[N<<2], h[N], cost[N<<2];
int ans, _mx[N], mx[N];

void add (int a, int b, int w) {
	cost[idx] = w, e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dp (int x, int fa) {
	
	for (int i = h[x];~i;i = ne[i]) {
		int j = e[i];
		if (j == fa) continue;
		dp (j, x);
		if (mx[x] <= mx[j] + cost[i])
			_mx[x] = mx[x],
			mx[x] = mx[j] + cost[i];
		else if (_mx[x] < mx[j] + cost[i])
			_mx[x] = mx[j] + cost[i];
		ans = max (ans, _mx[x] + mx[x]);
	}
}

int main()
{
	memset (h, -1, sizeof h);
	cin >> m;
	for (int i = 1;i < m;i ++) {
		cin >> u >> v >> w;
		add (u, v, w);
		add (v, u, w);
	} 
	
	dp (1, 0);
	cout << ans * 10 + ans * (ans + 1) / 2 << endl;

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值