春天阳光灿烂,小宋突然心血来潮想骑自行车去学校外面玩,但是到达目的地的路线不止一条,他想尽快的到达目的地,又能不需要骑太远的路, 你能帮助他吗?
输入格式:
输入包含一个测试数据,第一行有三个整数n(2 <= n <= 1000),途中可能经过的地点的个数,地点编号1~n;m(1 <= m <= 10000),为路径的条数;d(2 <= d <= n),目的地编号;其中学校为起点,默认为1。接下来m行: x y time dist , x y表示地点x,y是可以相互到达的,time,dist分别表示x到y或y到x的时间,距离。
输出格式:
按如下格式输出“花费的时间+空格+要骑的距离+空格+从学校到达目的地的路径”,路径中的两个地点之间以箭头(->)分隔。(具体见输出样例)
输入样例:
在这里给出一组输入。例如:
7 8 7
1 2 1 1
1 3 1 1
2 4 1 2
3 4 1 1
4 5 1 2
4 6 1 1
5 7 1 1
6 7 2 1
输出样例:
在这里给出相应的输出。例如:
4 5 1->3->4->5->7
想要解决这道题,大家需要先了解最重要的Dijkstra算法,那Dijkstra算法究竟该如何来理解呢?
由于本节目的是解决该问题,对于基础的Dijkstra算法我给出以下提示:(大家先要去了解最基本的Dijkastra算法程序):
①:到一个点时,选择这个点到各点的最小值
②:松弛,更新所有值,最小的为本阶段终点的最小单源路径。
Dijkstra算法的基础程序:(为了方便大家了解这道题如何用Dijkstra算法来求解,所以,我选择在解决本题的代码中提炼出基础算法的模型,大家可以跟后面解题的程序进行比较,明白怎么来化用基本的Dijkstra算法。)
void Dijkstra()
{
int visited[n + 1];
for(int i = 1;i < n + 1;i++)
{
visited[i] = 0;
}
visited[1] = 1;
Time[1] = 0;
while(1)
{
int MinTime = 0x3f3f3f;
int u = -1;
for(int i = 1;i <= n;i++)
{
if((visited[i] != 2) && (Time[i] < MinTime))
{
MinTime = Time[i];
u = i;
}
}
if(u == -1)
{
break;
}
visited[u] = 2;
for(int i = 1;i < n + 1;i++) //松弛
{
if(visited[i] != 2 && G[u][i].Time != INFTY)
{
if(Time[i] > Time[u] + G[u][i].Time)
{
Time[i] = Time[u] + G[u][i].Time;
visited[i] = 1;
pre[i] = u;
}
}
}
}
}
那么解决本题该如何做呢?很简单,这道题最本质的实际上是双目标,保证时间最短,距离最短,但这道题很明显,距离最短是在时间最短的前提下,所以只需要判断当有多条路径都最短时间时,哪个是最短距离。所以咱们只需要在时间相等的地方加个距离判断的条件即可。如下:
void Dijkstra()
{
int visited[n + 1];
for(int i = 1;i < n + 1;i++)
{
visited[i] = 0;
}
visited[1] = 1;
Dist[1] = 0;
Time[1] = 0;
while(1)
{
int MinDistance = 0x3f3f3f;
int MinTime = 0x3f3f3f;
int u = -1;
for(int i = 1;i <= n;i++)
{
if((visited[i] != 2) && (Time[i] < MinTime))
{
MinTime = Time[i];
MinDistance = Dist[i];
u = i;
}
else if((visited[i] != 2) && (Time[i] == MinTime))
{
if (MinDistance <= Dist[i])
{
MinTime = Time[i];
MinDistance = Dist[i];
u = i;
}
}
}
if(u == -1)
{
break;
}
visited[u] = 2;
for(int i = 1;i < n + 1;i++) //松弛
{
if(visited[i] != 2 && G[u][i].Time != INFTY)
{
if(Time[i] > Time[u] + G[u][i].Time)
{
Time[i] = Time[u] + G[u][i].Time;
Dist[i] = Dist[u] + G[u][i].dist;
visited[i] = 1;
pre[i] = u;
}
}
}
}
}
原本如果新选择的点和最短时间相同时,我们不会更换其路径,但这时我们只需要加个判断距离的条件即可。
完整代码如下:
#include <iostream>
#include <algorithm>
#define INFTY 1<<21
using namespace std;
typedef struct _Weight
{
int Time;
int dist;
}Weight;
Weight G[10001][10001];
int pre[10001];
void Dijkstra(int Dist[],int n,int Time[])
{
int visited[n + 1];
for(int i = 1;i < n + 1;i++)
{
visited[i] = 0;
}
visited[1] = 1;
Dist[1] = 0;
Time[1] = 0;
while(1)
{
int MinDistance = 0x3f3f3f;
int MinTime = 0x3f3f3f;
int u = -1;
for(int i = 1;i <= n;i++)
{
if((visited[i] != 2) && (Time[i] < MinTime))
{
MinTime = Time[i];
MinDistance = Dist[i];
u = i;
}
else if((visited[i] != 2) && (Time[i] == MinTime))
{
if (MinDistance >= Dist[i])
{
MinTime = Time[i];
MinDistance = Dist[i];
u = i;
}
}
}
if(u == -1)
{
break;
}
visited[u] = 2;
for(int i = 1;i < n + 1;i++) //松弛
{
if(visited[i] != 2 && G[u][i].Time != INFTY)
{
if(Time[i] > Time[u] + G[u][i].Time)
{
Time[i] = Time[u] + G[u][i].Time;
Dist[i] = Dist[u] + G[u][i].dist;
visited[i] = 1;
pre[i] = u;
}
}
}
}
}
void shortPathPre(int s, int v){
if(s == v){
cout << s;
return;
}
shortPathPre(s, pre[v]);
cout<<"->";
cout << v;
}
int main()
{
int n,m,end;
cin>>n>>m>>end;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
G[i][j].Time = INFTY;
G[i][j].dist = INFTY;
}
}
while(m--)
{
int x1,x2,time,distance;
cin>>x1>>x2>>time>>distance;
G[x1][x2].Time = time;
G[x1][x2].dist = distance;
G[x2][x1].Time = time;
G[x2][x1].dist = distance;
}
int Dist[n + 1];
int Time[n + 1];
for(int i = 1;i < n + 1;i++)
{
Dist[i] = INFTY;
Time[i] = INFTY;
}
Dijkstra(Dist,n,Time);
cout<<Time[end]<<' '<<Dist[end]<<' ';
shortPathPre(1,end);
return 0;
}