题目
样例输入
4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3
样例输出
1 2 4
2
5
思路
这个题目需要求最短路问题,显然Dijkstra算法可以解决。
Dijkstra算法
对源点s到达所有点的距离 dis[i] 初始化为inf,将dis[s]标记为0。
将s点加入最小堆(堆中按dis[i]排序)
选取堆顶元素(即dis[i]最小的元素),对其邻接点进行松弛,松弛操作为,判断dis[x] + w < dis[y] ?
如果松弛成功,并且y点从未出过堆,将y点加入堆。重复该过程,直到堆空。
回到本题
法一
从起点开始进行一次Dijkstra算法,从终点进行一次;
枚举每一条商业线(u,v,w),时间最短为{dis1[u] + dis2[v] + w, dis1[v] + dis2[u] + w, dis1[e]}。
法二:分层图实现
因为可以选择一条快速线,所以可以将图分为两层,对每一条快速线添加从第一层u到第二层v的单向边;
并且添加从第一层终点到第二层终点的单向边,设权重为0。
将起点设为第一层起点,将终点设为第二层终点,然后进行Dijkstra算法。
思考
法一思路上较为清晰,法二是分层图的思想,不好理解。
在输出上,法一pre1数组上的输出是通过递归后需要反向,而pre2数组上通过递归应该正向,对输出的处理比较麻烦;
而法二输出简洁明了。
以及在将点插入堆的时候,将dis取成相反数,就可以把最大堆当成最小堆使用。
雷
在Dijkstra算法中堆的数据类型为pair<int, int>,但我将first设为了点,忽略了pair在进行排序的时候是按第一关键字排序的,想当然的觉得它应该按边排序...然后就悲剧了...疯狂wa;
Dijkstra算法不能处理负边。
代码(法一实现)
#include<iostream>
#include<queue>
#include<algorithm>
#include<stack>
#include<cstring>
#define MAXN 2020
#define inf 100000000
using namespace std;
priority_queue<pair<int, int> > q;
stack<int> sta;
int dis1[MAXN], dis2[MAXN], pre1[MAXN], pre2[MAXN];
int vis1[MAXN], vis2[MAXN];
struct Edge
{
int u, v, w, next;
}Edge[2 * MAXN];
bool cmp(struct Edge &a, struct Edge &b)
{
return a.w < b.w;
}
int head[MAXN];
int total = 1;
void init()
{
total = 1;
for (int i = 0; i < MAXN; i++)
head[i] = -1;
}
void addEdge(int uu, int vv, int ww)
{
Edge[total].u = uu;
Edge[total].v = vv;
Edge[total].w = ww;
Edge[total].next = head[uu];
head[uu] = total;
total++;
}
void dijkstra(int s, int n, int dis[], int vis[], int pre[])
{
while (!q.empty()) q.pop();//先清空堆
//初始化
for (int i = 0; i < n; i++)
{
vis[i] = 0;
dis[i] = inf;
pre[i] = -1;
}
dis[s] = 0;
q.push(make_pair(0, s));
while (!q.empty())
{
int x = q.top().second;//最小边邻接的点
q.pop();
if (vis[x] == 1) continue;//x点已经标记过
vis[x] = 1;
for (int i = head[x]; i != -1; i = Edge[i].next)
{
int y = Edge[i].v;
int w = Edge[i].w;
if (dis[y] > dis[x] + w)
{
dis[y] = dis[x] + w;
q.push(make_pair(-dis[y], y));
pre[y] = x;
}
}
}
}
void pree1(int u)
{
if (pre1[u] != -1)
{
sta.push(pre1[u]);
pree1(pre1[u]);
}
else
{
while (!sta.empty())
{
cout << sta.top() << " ";
sta.pop();
}
}
}
void pree2(int u)
{
if (pre2[u] != -1)
{
sta.push(pre2[u]);
cout << " " << pre2[u];
pree2(pre2[u]);
}
}
void output(int u, int v)
{
while (!sta.empty()) sta.pop();
if (u == v)
{
//cout << "ye" << endl;
pree1(u);
cout << u;
}
else
{
pree1(u);
cout << u << " " << v;
pree2(v);
}
cout << endl;
}
int main()
{
int n, s, e, m, k, x, y, z, xx, yy, zz;
int u, v;
bool is = 0;
while (cin >> n >> s >> e)
{
int min = inf;
if (is) cout << endl;
else is = 1;
cin >> m;
init();
for (int i = 0; i < m; i++)
{
cin >> x >> y >> z;
addEdge(x, y, z);
addEdge(y, x, z);
}
dijkstra(s, n + 1, dis1, vis1, pre1);
dijkstra(e, n + 1, dis2, vis2, pre2);
cin >> k;
for (int i = 0; i < k; i++)
{
cin >> xx >> yy >> zz;
int uu = dis1[xx] + dis2[yy] + zz;
int vv = dis1[yy] + dis2[xx] + zz;
if (uu < min)
{
min = uu;
u = xx;
v = yy;
}
if (vv < min)
{
min = vv;
u = yy;
v = xx;
}
}
if (dis1[e] < min)
{
min = dis1[e];
u = v = e;
}
output(u, v);
if (u == e && v == e)
cout << "Ticket Not Used" << endl;
else
cout << u << endl;
cout << min << endl;
}
}