P1099 樹網的核
阿新 • • 發佈:2019-02-07
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 樹網的核