1. 程式人生 > >P1099 樹網的核

P1099 樹網的核

cpp min inf left 開始 [1] als pan 數據

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;
}

P1099 樹網的核