题意:邦德要从一个城市到达另一个城市,但是每条边都有危险值,bond需要找出一条路,是的路上的危险值最小,但是从u到v的一条路的危险值取决于这条路上的边的危险值的最大值。也就是说需要找到一条路,使得该路上最长边最短,就是最小瓶颈生成树了
因为要多次访问,所以的话先预处理。因为最短路一定是在最小生成树里的,所以,先求最小生成树,然后把最小生成树改成有根树,有根树中引入层的概念,根据到根节点的距离,分层,如果两个点在同一层,就可以从两边同时找向上层遍历来,因为从两边遍历,所以更省时间,因为层数相同,所以任意两点在向上层遍历的时候必会有祖先相同(此时两点之间就找到了一条路了,而且此路必然就是所求的路了)的情况,此时就找左边点到相同祖先的路的最大边,以及右边的点到相同祖先节点的路的最大边,然后取最大值
这题的话还能用二进制来优化时间,但是看不懂,所以就没用二进制
#include<stdio.h>
#include<string.h>
#include<queue>
#define MEM(x,y) memset(x,y,sizeof(x))
#define max(x,y) x<y?y:x
#define INF 51000
#define Max 1000000000
using namespace std;
struct edge //整个图中的边
{
friend bool operator <(edge x,edge y)
{
return x.len > y.len;
}
int x,y,len;
};
struct MSTedge //最小生成树中的边
{
int x,len,next;
}MSTe[2*INF];
int n,m,q;
int cnt;
int parent[INF],head[INF]; //head[]存以该点为起点的边在边集中的下标
priority_queue<edge>Q; //存储整个图的边
//将MST变成有根树时使用的变量
int father[INF],dis[INF]; //fatehr[]用来记录该节点在有根树中的父节点,dis[]用来记录该点的层数
int cost[INF]; //cost[]用来记录该点到其父节点的距离
void addMSTedge(int x,int y,int len)
{
MSTe[cnt].x = y;
MSTe[cnt].len = len;
MSTe[cnt].next = head[x];
head[x] = cnt++;
}
int root(int x)
{
if(parent[x] == -1)return x;
else return parent[x] = root(parent[x]);
}
void merge(int x,int y)
{
x = root(x);
y = root(y);
parent[x] = y;
}
void kruskal()
{
cnt = 0; //MST中边的下标
int jishu = 0; //计数
while(!Q.empty())
{
edge t = Q.top();
Q.pop();
if(root(t.x) != root(t.y))
{
merge(t.x,t.y);
jishu++;
addMSTedge(t.x,t.y,t.len);
addMSTedge(t.y,t.x,t.len);
}
if(jishu == n-1) //MST中有了n-1条边
break;
}
}
void roottree() //建立有根树
{
int flag[INF]; //标记MST的该点是够遍历过
MEM(flag,0);
MEM(dis,0);
MEM(father,-1);
MEM(cost,0);
queue<int>que;
while(!que.empty())
que.pop();
que.push(1); //以1为根利用bfs建立有根树
flag[1] = 1;
dis[1] = 0;
while(!que.empty())
{
int t = que.front();
que.pop();
for(int i = head[t] ; i != -1; i = MSTe[i].next)
{
int temp = MSTe[i].x; //该边的末点
if(flag[temp] == 0)
{
flag[temp] = 1; //标记该点已经遍历过了
father[temp] = t; //更新父节点
cost[temp] = MSTe[i].len;
dis[temp] = dis[t] + 1; //层数加一
que.push(temp);
}
}
}
}
void solve(int x,int y)
{
int maxx = -1,maxy = -1; //初始化两个点到根节点的路的最大边权
if(dis[x] > dis[y]) //如果x的层数大于y的,即x在y下面
{
while(dis[x] > dis[y])
{
maxx = max(maxx,cost[x]); //更新这条路的最大边权
x = father[x];
}
}
else if(dis[y] > dis[x])
{
while(dis[y] > dis[x])
{
maxy = max(maxy,cost[y]); //更新这条路的最大边权
y = father[y];
}
}
//此时x,y层数相同,然后就可以两边同时向上遍历,更快
while(x != y) //当x,y的祖先相同时,就结束算法
{
maxx = max(maxx,cost[x]); //两个节点同时往上走
x = father[x];
maxy = max(maxy,cost[y]);
y = father[y];
}
printf("%d\n",max(maxx,maxy));
}
int main()
{
int nnn= 0;
while(scanf("%d%d",&n,&m) != EOF)
{
if(nnn++ != 0)
printf("\n");
MEM(parent,-1);
MEM(head,-1);
MEM(MSTe,0);
while(!Q.empty())
Q.pop();
for(int i = 1; i <= m ; i++)
{
edge t;
scanf("%d%d%d",&t.x,&t.y,&t.len);
Q.push(t);
}
kruskal(); //求最小生成树MST
roottree(); //把MST变成有根树
scanf("%d",&q);
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
solve(x,y);
}
}
return 0;
}