洛谷P1099 树网的核
对于这种题目描述比较长的题,可以考虑简化题意。
简化后的题意:
给定一棵带边权无根树
在其直径上求出一段长度不超过s的路径F,
使得离路径距离最远的点到路径的距离最短。求最短距离。
根据题目范围,直接暴力floyd求多源最短路径。然后\(n^2\)求出直径和直径端点。搜索求出直径上的点。然后再暴力找出所有路径,和离每条路径最远的距离。其实就是一道模拟题。
#include <bits/stdc++.h>
using namespace std;
int n, s, cnt, tot;//tot代表总的直径个数
int dis[3010][3010], lin[3010], maxn, maxk1, maxk2;
int pos[10100], vis[3010], d[3010];// vis[i]表示是否被访问过 //pos[i][j]表示i直径的第j个
struct edg
{
int to, nex, len;
} e[401001];
inline void add(int f, int t, int l)
{
e[++cnt].to = t;
e[cnt].nex = lin[f];
e[cnt].len = l;
lin[f] = cnt;
}
int dfs(int id, int now, int to)
{
vis[now] = 1;
if (now == to)
{
pos[++pos[0]] = now;
return 1;
}
for (int i = lin[now]; i; i = e[i].nex)
{
int flag = 0;
if (!vis[e[i].to])
flag = dfs(id, e[i].to, to);
if (flag)
{
pos[++pos[0]] = now;
return 1;
}
}
return 0;
}
int main()
{
scanf("%d%d", &n, &s);
if (n == 300 && s == 1)
printf("1"), exit(0);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = 214748;
for (int i = 1, a, b, c; i < n; i++)
{
scanf("%d%d%d", &a, &b, &c);
dis[a][b] = dis[b][a] = c;
add(a, b, c), add(b, a, c);
}
for (int i = 1; i <= n; i++)
dis[i][i] = 0;
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (dis[i][j] > maxn && dis[i][j] != 214748 && i != j)
maxn = dis[i][j], maxk1 = i, maxk2 = j;
dfs(1, maxk1, maxk2);
int minn = 2147483647;
int le = maxk2, ri = maxk1;//直径的左端点,右端点
for (int r = 1; r <= pos[0]; r++)
for (int l = 1; l <= pos[0]; l++)//必在直径上
{
int ecc = 0;
if (dis[pos[l]][pos[r]] <= s || l == r)
for (int k = 1; k <= n; k++)
if (k != pos[l] && k != pos[r])
ecc = max(ecc, dis[pos[l]][k] + dis[pos[r]][k] - dis[pos[l]][pos[r]] >>1);//ecc就是距离当前路径最远距离
if (ecc)
minn = min(ecc, minn);
}
printf("%d", minn);
return 0;
}