题意:
有个人现在要从起点到达终点,有n条经济线和m条商务线连接着N个点。现在他有一条商务线的票,即可以乘坐一条商务线路。每条线路给定所需要花费的时间,求出从起点到达终点花费时间最少的线路。
input:
输入包含多组数据。每组数据第一行为 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),表示在xy之间有一条线路,需要 花费Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000),接下来 K 行是商业线路段的描述,格式同经济线。注意:所有的线路都是双向的,并且商务线可以乘坐可以不乘坐。
output:
对于每组数据,输出3行。第一行依次给出所经过的点的序号,第二行是 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行花费的总时间。
思路:
因为商务只可以乘坐一次,故枚举每条商务线,得到使用这条商务线的最短时间和路线,枚举结束后,得到使用商务线的最短路径和花费的时间。最后和不适用商务线进行对比,得到花费时间最少的那条线路。 做法:在输入经济线的时候,构建图,并从起点S和终点E,跑一遍dij算法,得到从S和E开始,到达每一个点的所花费的最短时间的路线。在每得到一条商务线的时候x,y,z。即可得到dis[x]+dis[y]+z的值和dis[y]+dis[x]+z的值,如果所花费时间比现在最小的时间少,则更新记录下来的那条记录。最后得到使用商务座的最少时间的线路,再与不使用的进行比较,得到花费时间最少的那条线路,(提醒自己:前向星中最终的边数会是M的两倍)。
dij算法:首先有vis[]数组,记录该点是否已经访问到,以及dis[]数组记录当前的路径长度,lu[]数组,记录前序点。首先将起点放进堆中,(最小堆可以通过向最大堆中的数变为负数即可。)依次取出当前堆中,路径最短的,然后进行标记,在以这点开始,更新它所能到达的所有点,(如果dis1[t1] > dis1[x] + w1,即更新),包括该点的路径长度和前序点。当所有点更新完后,即完成。
输出:该题输出格式要求巨多,要特别注意。另外,从起点开始的记录的前序点数组lu1[]和从终点开始记录的前序点数组lu2[]最终输出的顺序是不同的。
题目代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
const int inf = 1e8;
int N, S,E;
bool vis1[505]; //标记
bool vis2[505];
int dis1[505]; //距离
int dis2[505];
int lu1[505]; //前序点
int lu2[505];
int minq, minz; //走的商业的线路的起点终点
int minw; //所走的最短时间
int minlu[505]; //记录路线
int con;
struct Edge {
int to, next, w;
}e[2050];
int head[505];
int tot;
priority_queue< pair<int, int> > q; //最大堆
void add_edge(int t, int f, int W) {
e[tot].to = t;
e[tot].next = head[f];
head[f] = tot;
e[tot].w = W;
tot++;
}
void dij1() {
while (q.size()) q.pop();
//初始化
for (int i = 1; i <= N; i++) {
vis1[i] = 0, dis1[i] = inf;
}
dis1[S] = 0;
q.push(make_pair(0, S));
while (q.size()) {
int x = q.top().second;
q.pop();
if (vis1[x]) continue; //已经加入过堆
vis1[x] = 1;
int i = head[x];
while (i != -1) {
int t1 = e[i].to;
int w1 = e[i].w;
if (dis1[t1] > dis1[x] + w1) {
dis1[t1] = dis1[x] + w1;
lu1[t1] = x;
q.push(make_pair(-dis1[t1], t1));
}
i = e[i].next;
}
}
}
void dij2() {
while (q.size()) q.pop();
//初始化
for (int i = 1; i <= N; i++) {
vis2[i] = 0, dis2[i] = inf;
}
dis2[E] = 0;
q.push(make_pair(0, E));
while (q.size()) {
int x = q.top().second;
q.pop();
if (vis2[x]) continue; //已经加入过堆
vis2[x] = 1; //标记
int i = head[x];
while (i != -1) {
int t1 = e[i].to;
int w1 = e[i].w;
if (dis2[t1] > dis2[x] + w1) {
dis2[t1] = dis2[x] + w1;
lu2[t1] = x;
q.push(make_pair(-dis2[t1], t1));
}
i = e[i].next;
}
}
}
//起点终点
void LU1(int x, int y) { //找出路线
con = 0;
while (y != x) {
minlu[con] = y;
con++;
y = lu1[y];
}
cout << x;
for (int i = con - 1; i >= 0; i--) {
cout << " " << minlu[i];
}
}
void LU2(int x, int y) { //找出路线
con = 0;
while (y != x) {
minlu[con] = y;
con++;
y = lu2[y];
}
for (int i = 0; i < con; i++) {
cout<<" "<<minlu[i];
}
cout<<" "<< x;
}
int main() {
ios::sync_with_stdio(0);
int M,K;
//车站数,起点,终点数
int ret = 0;
while (cin>>N && cin>>S && cin>>E) {
tot = 0;
if (ret == 0) {
ret = 1;
}
else {
cout<<endl;
}
minw = inf; //记录最终路线
minq = -1;
minz = -1;
cin >> M;
int x, y, z;
for (int i = 1; i <= N; i++) {
head[i] = -1;
}
for (int i = 0; i < M; i++) {
cin >> x >> y >> z;
add_edge(y, x, z);
add_edge(x, y, z);
}
dij1();
dij2();
cin >> K;
for (int i = 0; i < K; i++) {
cin >> x >> y >> z;
if (minw > (dis1[y] + dis2[x] + z)) {
minq = y, minz = x, minw = dis1[y] + dis2[x] + z;
}
if(minw > dis1[x] + dis2[y] + z){
minq = x, minz = y, minw = dis1[x] + dis2[y] + z;
}
}
if (minw > dis1[E]) {
LU1(S, E);
cout << endl;
cout << "Ticket Not Used"<< endl;
cout << dis1[E] << endl;;
}
else {
LU1(S, minq);
LU2(E, minz);
cout << endl;
cout << minq << endl;
cout << minw << endl;
}
}
return 0;
}