洛谷P4408 逃学的小孩

题目

求树的直径,因为任意两个居住点之间有且只有一条通路,所以这是一棵树。

根据题意父母先从C去A,再去B,或者反过来。

我们一定是要让A到B最大,也要让C到A和B的最小值最大。

AB最大一定就是直径了。

CA最大直接先求出任意一条直径的两个端点,必定一个是A、一个是B。然后枚举C,找到最大的(min(CA,CB))然后与AB相加即可。

#include <bits/stdc++.h>
#define int long long  
#define N 1010011
using namespace std;
struct edg {				
	int from, to, nex, len;// vis表示是否在最大生成树上 
}e[N * 2];		
int n, m, cnt, ans, ansk, lin[N], dis1[N], vis[N];// 这必是一颗树
int disk[N];
inline void add(int f, int t, int c)
{
	e[++cnt].nex = lin[f];
	e[cnt].to = t;
	e[cnt].len = c;
	lin[f] = cnt;
}
void dfs(int now)
{
	vis[now] = 1;
	for (int i = lin[now]; i; i = e[i].nex)
	{
		int to = e[i].to;
		if (!vis[to])
		{
			dis1[to] = dis1[now] + e[i].len;
			ans = max(ans, dis1[to]);
			dfs(to);
		}			
	}
}
void dfs2(int now)
{
	vis[now] = 1;
	for (int i = lin[now]; i; i = e[i].nex)
	{
		int to = e[i].to;
		if (!vis[to])
		{
			disk[to] = disk[now] + e[i].len;
			if (disk[to] > ans)
			 	ans = max(ans, disk[to]), ansk = to;
			dfs2(to);
		}			
	}
}
signed main()
{
	scanf("%lld%lld", &n, &m);
	for (int i = 1, a, b, c; i <= m; i++)
	{
		scanf("%lld%lld%lld", &a, &b, &c);
		add(a, b, c);
		add(b, a, c);
	}
	dfs(1);
	int maxn = 0, maxk = 0;	
	for (int i = 2; i <= n; i++)
	{
 		if (dis1[i] > maxn)
 		{
			maxn = dis1[i];
			maxk = i;
 		} 
	}
 	ans = 0; 
 	maxn = 0;
	memset(vis, 0, sizeof(vis));
 	dfs2(maxk);
	memset(vis, 0, sizeof(vis));
 	memset(dis1, 0, sizeof(dis1));
 	dfs(ansk);
 	for (int i = 1; i <= n; i++)
 	{
 		if (i == maxk || i == ansk) continue;
 		maxn = max(maxn, min(dis1[i], disk[i]));
 	}
 	printf("%lld", maxn + ans);//ans是树的直径大小。
}

/*
4 3
1 2 1
1 3 1
1 4 2
5
*/
原文地址:https://www.cnblogs.com/liuwenyao/p/11728214.html