首先要用人话来翻译这道题:
给你一个无根树和一个非负整数\(s\),求直径上的一段长度不超过\(s\)的路径\(F\),使树上结点到\(F\)距离最大值的最小值。
首先当然要求直径辣!因为数据小,直接大力两次dfs就可以求出直径。
然后怎么在直径上取完所有长度不超过\(s\)的路径?这道题的关键就在这里。
答案是尺取法(two pointers)。从直径的一端开始,慢慢尺取到直径的另一端为止。
尺取法的核心思想还是那句话:
满足条件时移动右端点,不满足条件才移动左端点。
因为\(n \leq 300\)及其小,可以直接用数组求出以某个结点为根,与所有结点的距离。
所以在预处理之后就可以暴力更新这个偏心距。
代码:
#include<bits/stdc++.h>
const int maxn = 305;
const int INF = 0x3f3f3f3f;
std::vector<std::pair<int,int> > G[maxn];
int dist[maxn][maxn];
int fa[maxn], weight[maxn];
bool vis[maxn];
int pxj[maxn];
int id1, id2;
int n, S;
void dfs1(int idx, int u, int f, int dep) {
dist[idx][u] = dep;
for(auto it : G[u]) {
int v = it.first, w = it.second;
if(v == f) continue;
dfs1(idx, v, u, dep + w);
}
}
void dfs(int u, int f) {
fa[u] = f;
for(auto it : G[u]) {
int v = it.first, w = it.second;
if(v == f) continue;
weight[v] = w;
dfs(v, u);
}
}
void init() {
for(int i = 1; i <= n; i++) {
dfs1(i, i, 0, 0);
}
int maxdep = -1, idx = -1;
for(int i = 1; i <= n; i++) {
if(dist[1][i] > maxdep) {
maxdep = dist[1][i]; idx = i;
}
}
id1 = idx;
maxdep = idx = -1;
for(int i = 1; i <= n; i++) {
if(dist[id1][i] > maxdep) {
maxdep = dist[id1][i]; idx = i;
}
}
id2 = idx;
dfs(id1, 0);
}
int cal_pxj() {
memset(pxj, 0x3f, sizeof pxj);
for(int i = 1; i <= n; i++) if(vis[i]) {
for(int j = 1; j <= n; j++) {
pxj[j] = std::min(pxj[j], dist[i][j]);
}
}
int ret = -INF;
for(int i = 1; i <= n; i++) if(!vis[i]) ret = std::max(ret, pxj[i]);
return ret;
}
void solve() {
std::vector<int> points, edges;
int size = 0;
for(int i = id2; i; i = fa[i]) {
points.push_back(i);
edges.push_back(weight[i]);// weight[id1] = 0
//printf("points[%d]: %d, edges[%d]: %d\n", size, points[size], size, edges[size]);
size++;
}
int len = 0, left = 0, right = 0; vis[points[0]] = true;
int ans = INF;
while(right < size) {
//printf("(left, right): %d %d, len = %d\n", points[left], points[right], len);
if(len >= 0 && len <= S) ans = std::min(ans, cal_pxj());
while(len + edges[right] <= S && right + 1 < size) {
len += edges[right];
right++;
vis[points[right]] = true;
//printf("(left, right): %d %d, len = %d\n", points[left], points[right], len);
if(len >= 0 && len <= S) ans = std::min(ans, cal_pxj());
}
len -= edges[left];
vis[points[left]] = false;
left++;
if(left == size) break;
}
printf("%d\n", ans);
}
int main() {
scanf("%d %d", &n, &S);
for(int i = 1; i < n; i++) {
int u, v, w; scanf("%d %d %d", &u, &v, &w);
G[u].push_back(std::make_pair(v, w));
G[v].push_back(std::make_pair(u, w));
}
init();
solve();
return 0;
}
原文:https://www.cnblogs.com/Garen-Wang/p/10354346.html