P1099 树网的核[two-pointers][树]

P1099 树网的核

首先要用人话来翻译这道题:

给你一个无根树和一个非负整数\(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;
}
posted @ 2019-02-07 00:43  Garen-Wang  阅读(127)  评论(0编辑  收藏  举报