题意
众所周知,TT 有一只魔法猫。
今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!
输入输出说明
输入:
输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E (2 ≤ N ≤ 500, 1 ≤ S, E ≤ 100),即猫猫快线中的车站总数,起点和终点(即喵星机场所在站)编号。
下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。
接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
输出:
对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。
本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行
样例:
input:
4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3
output:
1 2 4
2
5
思路
-
首先这是一个求最短路径的问题,从家到机场;
-
其次路径分为经济线和商业线,需要做的就是通过选择适当的路线使得路径最短;
-
我们需要求最短路径,可以注意到我们可能需要在中间的某一条线换乘商业线,所以我们需要求出到从起点到每一个车站的最短路(为了加入商业线)我们就采用dijkstra算法;
-
利用dijkstra算法我们能求出到任何一个车站的经济线最短路径
-
为了考虑加入商业线使得整体最短路径最小,那么我们有3种选择
1 家到商业线的起点的最短路径(经济线)+商业线长度+商业线终点到机场的最短路径 (经济线) 2 到商业线的终点的最短路径(经济线)+商业线长度+商业线起点到机场的最短路径(经济线)(路是双向的) 3 不加商业线,家到机场的经济线最短路
-
第一种和第二种需要进行两次dijkstra算法,一次以起点为源点,一次以终点为源点;
-
这三种路线取最小值及就是最短路径长度
-
为了输出最短路径的车站,我们采用递归的方法,使用一个数组记住到车站的前导结点;
-
追溯路径的时候,有两个函数,一个是从X站到起点,一个Y站到终点(X,Y分别为商业线的起始站)
总结
- 我们在加入商业线以后应该注意的就是线路是双向的,1和2应该进行比较一下;
- 其次就是dijsktra不能解决负边问题;
- 在以终点为源点进行dijsktra算法的时候应该也在存储线路的数组中加入逆向线路长度;
- 在很多条商业线的时候,我们应该标记下我们选择的上述三种操作中的哪一种,还得记住我们采用的是哪一条商业线,应当记住起始站(为了递归追溯路径);
代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<string.h>
using namespace std;
const int N = 510;
const int inf = 50000;
int e[N][N];
int dis[N],dis1[N],path[N],path1[N],tol=0;
bool vis[N];
priority_queue<pair<int, int> >q;
void dijkstra(int s,int n)
{
path[s] = -1;//表明x为原点;
while (!q.empty())
q.pop();//清空队列
for (int i = 0; i <= n; i++)
vis[i] = false;
for (int i = 0; i <= n; i++)
dis[i] = inf;//初始化距离数组
dis[s] = 0;
q.push(make_pair(0, s));
while (!q.empty())
{
int x = q.top().second;
q.pop();
if (vis[x])
continue;
vis[x] = true;
for (int i = 1; i<=n; i++)
{
if (dis[i] > e[x][i] + dis[x])
{
dis[i] = e[x][i] + dis[x];
path[i] = x;
q.push(make_pair(-dis[i], i));
}
}
}
}
void dijkstra1(int s, int n)
{
path1[s] = -1;//初始点没有上一个结点;故路径数组记-1
while (!q.empty())
q.pop();//清空队列
for (int i = 0; i <= n; i++)
vis[i] = false;
for (int i = 0; i <= n; i++)
dis1[i] = inf;//初始化距离数组
dis1[s] = 0;
q.push(make_pair(0, s));
while (!q.empty())
{
int x = q.top().second;
q.pop();
if (vis[x])
continue;
vis[x] = true;
for (int i = 1; i <= n; i++)
{
if (dis1[i] > e[x][i] + dis1[x])
{
dis1[i] = e[x][i] + dis1[x];
path1[i] = x;
q.push(make_pair(-dis1[i], i));
}
}
}
}
void path_(int s,int temp)//从起点经过的车站
{
if (path[s] == -1)
{
cout << s << " ";
return;
}
path_(path[s],temp);
if (temp == s)
cout << s;
else
cout << s << " ";
}
void path_1(int s)//从终点经过的车站
{
if (path1[s] == -1)
{
cout << s ;
return;
}
cout <<s<<" ";
path_1(path1[s]);
}
void out_put(pair<int, int> min, int X1, int Y1, int S, int E,int n)
{
switch(min.first)
{
case 1:
{
path_(X1,E);
path_1(Y1);
cout << endl;
cout << X1 << endl;
break;
}
case 2:
{
tol = 0;
path_(Y1,E);
path_1(X1);
cout << endl;
cout << Y1 << endl;
break;
}
case 3:
{
tol = 0;
path_(E,E);
cout << endl;
cout << "Ticket Not Used" << endl;
break;
}
}
}
int main()
{
int n, S, E,K;
bool ans = false;
while (scanf("%d%d%d", &n, &S, &E) != EOF)
{
if(ans)
cout << endl;
ans = true;
int m, X, Y, Z, count = 1, count1 = 1;
cin >> m;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
e[i][j] = inf;
}
}
for (int i = 1; i <= m; i++) {//进行路径的初始化
scanf("%d%d%d", &X, &Y, &Z);
e[X][Y] = Z;
e[Y][X] = Z;
}
pair<int, int> sum;
sum.second = 0, sum.first = 0; //sum和sum1,min,min_i的first是为了记录哪一种路径,second记录总时间
pair<int, int> sum1;
sum1.second = 0, sum1.first = 0;
pair<int, int> min;
min.second =inf, min.first = 0;
int X1=0, Y1=0;//记住商务车线(因为商务可能有很多条,所以要一直记得最优是哪条)
dijkstra(S, n); //从起点开始进行一次单源最短路径的求取
dijkstra1(E, n); //从终点开始进行一次单源最短路径的求取
sum1.second = dis[E], sum1.first = 3; //3代表dis[E];没有使用商务线
cin >> K;
for (int i = 0; i < K; i++) //商务线的初始化
{
scanf("%d%d%d", &X, &Y, &Z);
//进行各种选项的比较,选出最优
if ((dis[X] + dis1[Y] + Z) >= (dis[Y] + dis1[X] + Z))
sum.second = dis[Y] + dis1[X] + Z, sum.first = 2; //2代表dis[Y] + dis1[X] + Z
else
sum.second = dis[X] + dis1[Y] + Z, sum.first = 1; //1代表dis[X] + dis1[Y] + Z
pair<int, int> min_i;
min_i.second = 0, min_i.first = 0;
if (sum.second >= sum1.second)
{
min_i = sum1;
}
else
min_i = sum;
//得到最优
if (min_i.second <= min.second)
{
min = min_i;
X1 = X; //记录最优时商务线
Y1 = Y;
}
}
out_put(min, X1, Y1, S, E,n); //进行输出
cout << min.second << endl;
}
return 0;
}