程设week7 B - TT 的旅行日记(Dijkstra算法)

题目

样例输入

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;
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值