【问题描述】
FC星有许多城市,城市之间通过一种奇怪的双向高速公路进行交流,每条公路都对行驶在上面的飞车限制了固定的速度,同时FC星人对飞车的“舒适度”有特殊要求,即乘坐过程中最高速度与最低速度的差越小乘坐越舒服,但对时间却没那么多要求。要注意的是FC人的飞车能瞬间提速或降速。现在需要你找出一条城市间的最舒适的路径。
【输入格式】
第一行有2个正整数N和M,表示有N个城市和M条双向高速公路。接下来的M行,每行是三个正整数x,y,speed,分别表示城市x、y间的高速公路上飞车必须以speed速度行驶。第m+2行是一个正整数Q,接下来Q行每行有2个正整数x,y,表示询问城市x到y之间限制的固定速度。
【输出格式】
有Q行,对应输入的Q个查询,第i行的整数表示对应查询的两个城市间的最高速与最低速的差的最小值,如果两个城市不能到达则输出-1。
【输入样例】
6 7
1 2 8
1 4 7
2 3 1
3 4 9
3 6 5
4 5 3
5 6 10
2
1 6
2 5
【输出样例】
4
5
【数据范围】
1<N<=300
M<=25000
Q<1000
0<=speed<=1000000000
【思路梳理】
这个题要求的是查找一条从出发城市到目的地城市之间的路径,使得该路径上最大权值和最小权值之间的差最小。涉及到搜索这样的路径的问题,首先想到的应该就是并查集:通过不断地将边按照一定顺序加入到空边图中,使得出发城市和目的地城市之间相互连通。既然所求的是边权的差值最小,那么可以通过枚举的方法,将路径上最小的显然我们有先将每一条边按照权值由小到大排序后,依次枚举限制速度最小、第二小、第三小,etc的边判断此时出发城市与目的地城市是否连通,若成立则记录下此时两者的差值。具体如下:
step1 从最小边开始,依次由小到大向空边图中添加,当添加到第j条边时,x和y属于同一个集合,表示已经添加的边能使x到达y,则令ans=min(ans,w[j]-w[1]);
step2从第二小边开始,依依次由小到大向空边图中添加,当添加到第j条边时,x和y属于同一个集合,表示已经添加的边能使x到达y,则令ans=min(ans,w[j]-w[2]);
......
最后的ans 为x到y路径上边差最小值的。
考虑一些特殊情况:目的地与出发城市为同一个顶点或者两者只有一条边连接,此时边的权值差为0。若两者始终不能够连通,那么就应当输出-1.
给出代码如下:
【CPP代码】
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 305
#define maxm 25005
#define inf 1000000005
using namespace std;
struct edge
{
int u,v,w;
};
vector<edge>E;
int n,m,q,fa[maxn];
int find(int x)
{
if(fa[x]==x) return x;
int root=find(fa[x]);
fa[x]=root;
return root;
}
void Union(int a,int b)
{
fa[find(a)]=find(b);
}
void initial()
{
for(int i=1;i<=n;i++) fa[i]=i;
}
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int calc(int s,int d)
{
int ans=inf;
for(int i=0;i<E.size();i++)
{
initial();
for(int j=i;j<E.size();j++)
{
int x=E[j].u,y=E[j].v;
Union(x,y);
if(find(s)==find(d))
{
ans=min(ans,E[j].w-E[i].w);
break;
}
if(find(s)!=find(d) && j==E.size()-1) return ans;//强大的剪枝:当某个时刻枚举完了所有的边之后,出发城市和目的地城市始终不能连通,说明某条必须的边已经枚举过,此时直接return即可
}
}
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
E.push_back((edge){a,b,c});
}
sort(E.begin(),E.end(),cmp);
cin>>q;
while(q--)
{
int s,d;
scanf("%d%d",&s,&d);
if(s==d){printf("0\n");continue;}
int cnt=calc(s,d);
if(cnt!=inf) printf("%d\n",cnt);
else printf("-1\n");
}
return 0;
}
FC星有许多城市,城市之间通过一种奇怪的双向高速公路进行交流,每条公路都对行驶在上面的飞车限制了固定的速度,同时FC星人对飞车的“舒适度”有特殊要求,即乘坐过程中最高速度与最低速度的差越小乘坐越舒服,但对时间却没那么多要求。要注意的是FC人的飞车能瞬间提速或降速。现在需要你找出一条城市间的最舒适的路径。
【输入格式】
第一行有2个正整数N和M,表示有N个城市和M条双向高速公路。接下来的M行,每行是三个正整数x,y,speed,分别表示城市x、y间的高速公路上飞车必须以speed速度行驶。第m+2行是一个正整数Q,接下来Q行每行有2个正整数x,y,表示询问城市x到y之间限制的固定速度。
【输出格式】
有Q行,对应输入的Q个查询,第i行的整数表示对应查询的两个城市间的最高速与最低速的差的最小值,如果两个城市不能到达则输出-1。
【输入样例】
6 7
1 2 8
1 4 7
2 3 1
3 4 9
3 6 5
4 5 3
5 6 10
2
1 6
2 5
【输出样例】
4
5
【数据范围】
1<N<=300
M<=25000
Q<1000
0<=speed<=1000000000
【思路梳理】
这个题要求的是查找一条从出发城市到目的地城市之间的路径,使得该路径上最大权值和最小权值之间的差最小。涉及到搜索这样的路径的问题,首先想到的应该就是并查集:通过不断地将边按照一定顺序加入到空边图中,使得出发城市和目的地城市之间相互连通。既然所求的是边权的差值最小,那么可以通过枚举的方法,将路径上最小的显然我们有先将每一条边按照权值由小到大排序后,依次枚举限制速度最小、第二小、第三小,etc的边判断此时出发城市与目的地城市是否连通,若成立则记录下此时两者的差值。具体如下:
step1 从最小边开始,依次由小到大向空边图中添加,当添加到第j条边时,x和y属于同一个集合,表示已经添加的边能使x到达y,则令ans=min(ans,w[j]-w[1]);
step2从第二小边开始,依依次由小到大向空边图中添加,当添加到第j条边时,x和y属于同一个集合,表示已经添加的边能使x到达y,则令ans=min(ans,w[j]-w[2]);
......
最后的ans 为x到y路径上边差最小值的。
考虑一些特殊情况:目的地与出发城市为同一个顶点或者两者只有一条边连接,此时边的权值差为0。若两者始终不能够连通,那么就应当输出-1.
给出代码如下:
【CPP代码】
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 305
#define maxm 25005
#define inf 1000000005
using namespace std;
struct edge
{
int u,v,w;
};
vector<edge>E;
int n,m,q,fa[maxn];
int find(int x)
{
if(fa[x]==x) return x;
int root=find(fa[x]);
fa[x]=root;
return root;
}
void Union(int a,int b)
{
fa[find(a)]=find(b);
}
void initial()
{
for(int i=1;i<=n;i++) fa[i]=i;
}
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int calc(int s,int d)
{
int ans=inf;
for(int i=0;i<E.size();i++)
{
initial();
for(int j=i;j<E.size();j++)
{
int x=E[j].u,y=E[j].v;
Union(x,y);
if(find(s)==find(d))
{
ans=min(ans,E[j].w-E[i].w);
break;
}
if(find(s)!=find(d) && j==E.size()-1) return ans;//强大的剪枝:当某个时刻枚举完了所有的边之后,出发城市和目的地城市始终不能连通,说明某条必须的边已经枚举过,此时直接return即可
}
}
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
E.push_back((edge){a,b,c});
}
sort(E.begin(),E.end(),cmp);
cin>>q;
while(q--)
{
int s,d;
scanf("%d%d",&s,&d);
if(s==d){printf("0\n");continue;}
int cnt=calc(s,d);
if(cnt!=inf) printf("%d\n",cnt);
else printf("-1\n");
}
return 0;
}