题目描述
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); dfs(1, 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; }