本篇题解基于简单易理解的算法
简单的阅读题面:
从x点出发到达y点所能运输最大的货物数量
不妨先简化一下题目,进行初步思考写一下暴力,在这里提供一个[简化版](https://www.luogu.org/problemnew/show/T42365)
想了解的同学可以了解一下
简化后的题目就是简单的套用单源最短路模板便可求解,例如在dij带堆优化O(nlogn)的情况下可以通过n <= 2,000 的数据
```
que.pop();
int p = k[x].size();
for (int j = 0; j < p; ++j){
int road = min(k[x][j].quan, dis[x]);
if (dis[k[x][j].x] < road){
dis[k[x][j].x] = road;
que.push(node(k[x][j].x, dis[k[x][j].x]));
}
```
先理解了就方便进行下一步思考,显然该题的xy位置不定,若是每一个点都求一遍单源最短路在O(n2logn)下是不能在n <= 100,000 , p <= 30,000 的数据通过的那么掏出纸和笔进行下一步思考
再从题面开始,重新进行解读:
1. 题目给了m条**有向边**
2. **存在x点与y点不连通的情况**
3. x点到y点运输货物最大量是x点到y点的路径**最小限制尽可能大**
如果有耐心的同学可以手动生成一个小数据图测试一下,大部分手推的同学或打开标签查看的同学不难发现,既然要尽可能避免权值较小的边,还要保证x点与y点联通
保证联通我们需要n-1条边,那么就**只要进行一次生成树,便可以排除所有实际运输中并不需要的边**
既然要生成树便写上并查集, 顺带解决了连通性问题此时将边数m优化为n-1
继续套用最短路算法:得分60
单源最短路显然无法满足需求,每一次求解都进行了nlogn次的计算过于繁杂
可是我们现在已经将图转化为多颗树了,真的需要继续进行求最短路吗?
该题不同于普通的最短路问题,最短路仅为x点到y点权值最小的一条边
注意了:**树上任意两点间有唯一的路径**
那何必又去求最短路了呢,这个时候有些看过别的题解的小朋友或看过标签的小朋友赶紧去搜索倍增算法敲个模板然后回来照着别的题解把这题A掉了
**然而在n <= 100,000的情况下真的要大费周章吗?**
**在OI考场上要发散自己的思维,在该题只需要x点到y点路径上最小权值的情况下,我们只需要简单的枚举法就可以通过该题**,事实证明效率还不错
没学过倍增的同学一样知道,x点到y点是需要经过他们的最近公共祖先的(字面意思)
插一小段存祖先的dfs代码
```
for (int i = 1; i <= n; ++i){
if (!bol[i]){
bol[i] = true;
dfs(i, 0);}
}
void dfs(int i, int cen){
int p = k[i].size();
bol[i] = true;
deep[i] = cen;
for (int j = 0; j < p; ++j){
int t = k[i][j].x;
if (!bol[t]){
fa[t].x = i;
fa[t].quan = k[i][j].quan;
dfs(t, cen+1);
}
}
}
```
然后直接算出x点到祖先路径上最小权值与y点到祖先路径上最小权值去两者之间最小值
解决_(:з」∠)_
本人AC代码:(无各种优化602ms)
```
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<time.h>
using namespace std;
int n, m, p, t1, t2, father[100010], dis[100010], deep[100010];
bool bol[100010];
struct bian{
int l, r, quan;
} f[50010];
struct node{
int x, quan;
node (int a=0, int b=0) : x(a), quan(b){
}
friend bool operator < (node a, node b){
return a.quan < b.quan;
}
};
vector <node> k[100010];
node fa[100010];
bool cmp(bian a, bian b){
return a.quan > b.quan;
}
int find(int x){
if (father[x] != x) father[x] = find(father[x]);
return father[x];
}
priority_queue <node> que;
void dfs(int i, int cen){
int p = k[i].size();
bol[i] = true;
deep[i] = cen;
for (int j = 0; j < p; ++j){
int t = k[i][j].x;
if (!bol[t]){
fa[t].x = i;
fa[t].quan = k[i][j].quan;
dfs(t, cen+1);
}
}
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 0; i <= n; ++i) father[i] = i;
for (int i = 0; i < m; ++i){
scanf("%d %d %d", &f[i].l, &f[i].r, &f[i].quan);
}
sort(f, f+m, cmp);
for (int i = 0; i < m; ++i){
int L = find(f[i].l);
int R = find(f[i].r);
if (L != R){
//printf("%d %d %d\n", f[i].l, f[i].r, f[i].quan);
father[R] = L;
k[f[i].l].push_back(node(f[i].r, f[i].quan));
k[f[i].r].push_back(node(f[i].l, f[i].quan));
}
}
for (int i = 1; i <= n; ++i){
if (!bol[i]){
bol[i] = true;
dfs(i, 0);}
}
scanf("%d", &p);
for (int i = 0; i < p; ++i){
scanf("%d %d", &t1, &t2);
if (find(t1) == find(t2)){
int deep1 = deep[t1], deep2 = deep[t2], minn = 999999999;
while (deep1 > deep2){
minn = min(fa[t1].quan, minn);
t1 = fa[t1].x;
--deep1;
}while (deep2 > deep1){
minn = min(fa[t2].quan, minn);
t2 = fa[t2].x;
--deep2;
}
while (t1 != t2){
minn = min(fa[t1].quan, minn);
minn = min(fa[t2].quan, minn);
t1 = fa[t1].x;
t2 = fa[t2].x;
}
printf("%d\n", minn);
}
else printf("-1\n");
}
}
```
代码亮点在哪里:
1. 多余的边直接筛去,只需要一颗最小生成树
2. 在不追求高效率的情况下枚举法即可通过本题数据