題目描述
A 國有 n 座城市,編號從 1 到 n,城市之間有 m 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 q 輛貨車在運輸貨物, 司機們想知道每輛車在不超過車輛限重的情況下,最多能運多重的貨物。
輸入輸出格式
輸入格式:
輸入文件名為 truck.in。
輸入文件第一行有兩個用一個空格隔開的整數 n,m,表示 A 國有 n 座城市和 m 條道
路。 接下來 m 行每行 3 個整數 x、 y、 z,每兩個整數之間用一個空格隔開,表示從 x 號城市到 y 號城市有一條限重為 z 的道路。意:x 不等於 y,兩座城市之間可能有多條道路。
接下來一行有一個整數 q,表示有 q 輛貨車需要運貨。
接下來 q 行,每行兩個整數 x、y,之間用一個空格隔開,表示一輛貨車需要從 x 城市運輸貨物到 y 城市,注意:x 不等於 y。
輸出格式:
輸出文件名為 truck.out。
輸出共有 q 行,每行一個整數,表示對於每一輛貨車,它的最大載重是多少。如果貨
車不能到達目的地,輸出-1。
輸入輸出樣例
4 3 1 2 4 2 3 3 3 1 1 3 1 3 1 4 1 3
3 -1 3
說明
對於 30%的數據,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000; 對於 60%的數據,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000; 對於 100%的數據,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。
分析:這道題目就是求x到y的路徑上最小值最大的權值,固定的方法就是最大生成樹,當然如果是最大值最小,那么就是最小生成樹.怎么求最大生成樹呢?首先要是一棵樹,兩個點之間只有唯一的路徑,怎么判斷兩個點是否連通呢?並查集!怎么要滿足“最大”呢?排序!那么就先從大到小排序,把不在一個連通塊的兩個點連起來就可以了.那么就要求最小值最大,怎么求呢?可以知道這條唯一的路徑是兩個點到它們的lca的和,怎么求lca呢?倍增!首先一次dfs記錄出f[i][j],g[i][j],即第i個節點的第2^j個祖先和所求的最小值最大的結果.同時記錄下每個節點的深度,注意,這個深度是越大越往下.為了求lca(a,b),假設a的深度比b的深度大,那么把a往上移,直到深度相同,同時更新ans,這個時候如果a==b了,那么就返回,然后再讓兩個點同時上移,如果兩個點上移之后相等了,因為我們是從大到小枚舉2^j的,所以求出的點可能在lca上面,所以當兩個祖先不是相同的才跳.那么跳到最后就跳到了lca的下一層,往上再跳一個就可以了.每次跳都要更新ans.
#include <cstdio> #include <cstring> #include <cmath> #include <iostream> #include <algorithm> using namespace std; const int maxn = 10010, maxm = 50010; int n, m, q, fa[maxn], head[maxn], cnt, h[maxn], f[maxn][20], g[maxn][20], ans = 1000000000; bool vis[maxn]; struct node { int x, y, z; }a[maxm]; struct node2 { int nextt, to, w; }e[maxn * 2]; bool cmp(node a1, node b) { return a1.z > b.z; } void dfs(int u, int depth) { vis[u] = true; h[u] = depth; for (int i = head[u]; i; i = e[i].nextt) if (!vis[e[i].to]) { f[e[i].to][0] = u; g[e[i].to][0] = e[i].w; dfs(e[i].to, depth + 1); } return; } int find(int x) { if (fa[x] != x) fa[x] = find(fa[x]); return fa[x]; } void add(int p, int q, int v) { e[++cnt].to = q; e[cnt].nextt = head[p]; head[p] = cnt; e[cnt].w = v; } void kruskal() { sort(a + 1, a + 1 + m, cmp); for (int i = 1; i <= n; i++) fa[i] = i; for (int i = 1; i <= m; i++) { int fx = find(a[i].x), fy = find(a[i].y); if (fx == fy) continue; fa[fx] = fy; add(a[i].x, a[i].y, a[i].z); add(a[i].y, a[i].x, a[i].z); } } int lca(int a1, int b1) { ans = 1000000000; if (a1 == b1) return 0; if (h[a1] < h[b1]) swap(a1, b1); int k = int(log2(h[a1])); for (int i = k; i >= 0; i--) if (h[a1] - (1 << i) >= h[b1]) { ans = min(ans, g[a1][i]); a1 = f[a1][i]; } if (a1 == b1) return ans; for (int i = k; i >= 0; i--) if (f[a1][i] && f[a1][i] != f[b1][i]) { ans = min(ans, min(g[a1][i], g[b1][i])); a1 = f[a1][i]; b1 = f[b1][i]; } ans = min(ans, min(g[a1][0], g[b1][0])); return ans; } int main() { memset(vis, false, sizeof(vis)); memset(head, 0, sizeof(head)); memset(f, 0, sizeof(f)); memset(g, 127, sizeof(g)); //INF scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z); kruskal(); scanf("%d", &q); for (int i = 1; i <= n; i++) if (!vis[i]) dfs(i, 1); for (int j = 1; (1 << j) <= n; j++) for (int i = 1; i <= n; i++) if (f[i][j - 1]) { f[i][j] = f[f[i][j - 1]][j - 1]; g[i][j] = min(g[i][j - 1], g[f[i][j - 1]][j - 1]); } for (int i = 1; i <= q; i++) { int x, y; scanf("%d%d", &x, &y); if (find(x) != find(y)) printf("-1\n"); else printf("%d\n", lca(x, y)); } return 0; }