题目描述
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入格式
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行三个整数 x,y,z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。
注意: x≠y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x,y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,保证 x≠y
输出格式
共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。
如果货车不能到达目的地,输出 −1。
输入输出样例
输入
4 3 1 2 4 2 3 3 3 1 1 3 1 3 1 4 1 3
输出
3 -1 3
说明/提示
对于 30% 的数据,1≤n<1000,1≤m<10,000,1≤q<1000;
对于 60% 的数据,1≤n<1000,1≤m<50000,1≤q<1000;
对于 100% 的数据,1≤n<10000,1≤m<50000,1≤q<30000,0≤z≤100000。
“左的错误”
一眼看到1≤n<10000就想到O(n*n)那我们可不可以每次询问的时候都直接跑一遍单源最短路?时间复杂度级别为O(n*n*log(n))
如果遇到前面跑过的点就直接输出,以此稍微优化一下
但是,这样的复杂度还是不能把娇弱的测评机哄开心qwq
思路
既然每次询问时临时处理可行性不高,那就想到预先全局处理,考虑往生成树方向思考
通过库斯凯尔算法可以得到一棵生成树,使得任意两点间的简单路径即为运量最大的路径
有了一棵树,只要找两点间的路径,想到lca
Code
#include<bits/stdc++.h>
#define INF 2147483647
using namespace std;
const int MAXN=50001;
int n,m;
struct inedg{
int x,y,z;
}a[MAXN];
bool cmp(inedg a,inedg b){
return a.z>b.z;
}
int f[MAXN];
int getfa(int x){
if(f[x]==x)return x;
return f[x]=getfa(f[x]);
}
int head[MAXN],En;
struct EDGE{
int nxt,to,val;
}e[MAXN];
void addedge(int x,int y,int z){
e[++En].nxt=head[x];
e[En].to=y;
e[En].val=z;
head[x]=En;
}
bool v[MAXN];
int fa[MAXN][17],dep[MAXN],w[MAXN][17];
void dfs(int x){
v[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int to=e[i].to;
if(!v[to]){
fa[to][0]=x;
dep[to]=dep[x]+1;
w[to][0]=e[i].val;
dfs(to);
}
}
}
int lca(int x,int y){
if(getfa(x)!=getfa(y)){
return -1;
}//别漏了这种情况
int ans=INF;
if(dep[y]>dep[x])swap(x,y);
for(int i=16;i>=0;--i){
if(dep[fa[x][i]]>=dep[y]){
ans=min(ans,w[x][i]);
x=fa[x][i];
}
}
if(x==y)return ans;
for(int i=16;i>=0;--i){
if(fa[x][i]!=fa[y][i]){
ans=min(ans,min(w[x][i],w[y][i]));
x=fa[x][i];
y=fa[y][i];
}
}
ans=min(ans,min(w[x][0],w[y][0]));
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<m;++i){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
}
sort(a,a+m,cmp);
for(int i=1;i<=n;++i){
f[i]=i;
}
for(int i=0;i<m;++i){
int X=a[i].x,Y=a[i].y;
int fx=getfa(X),fy=getfa(Y);
if(fx==fy)continue;
f[fx]=fy;
addedge(X,Y,a[i].z);
addedge(Y,X,a[i].z);
}//kruskal算法生成树
for(int i=1;i<=n;++i){
if(!v[i]){
fa[i][0]=i;
dep[i]=1;
w[i][0]=INF;
dfs(i);
}
}
for(int i=1;i<17;++i){
for(int j=1;j<=n;++j){
fa[j][i]=fa[fa[j][i-1]][i-1];
w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]);
}
}//作lca预处理
int Q;
scanf("%d",&Q);
while(Q--){
int t1,t2;
scanf("%d%d",&t1,&t2);
printf("%d\n",lca(t1,t2));
}//使用O(n*log(n))的复杂度抚慰测评机awa
return 0;
}
小结
这种题目考验多种算法较为灵活的运用,通过题目要求找到方向,发挥每个算法的优势,使时间复杂度控制在要求之内