最短路问题的思路拓展

Posted akyna-zh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最短路问题的思路拓展相关的知识,希望对你有一定的参考价值。

最短路径问题的复习整理(1)
最短路径问题的复习整理(2)
最短路径问题的复习整理–优化篇

邮差

题目描述

有一个邮递员要送东西,邮局在节点 1。他总共要送 n−1 样东西,其目的地分别是节点 2 到节点 n。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有 m 条道路。这个邮递员每次只能带一样东西,并且运送每件物品过后必须返回邮局。求送完这 n−1 样东西并且最终回到邮局最少需要的时间。

输入格式

第一行包括两个整数,n 和 m,表示城市的节点数量和道路数量。

第二行到第 (m+1) 行,每行三个整数,u,v,w,表示从 u 到 v 有一条通过时间为 w 的道路。

输出格式

输出仅一行,包含一个整数,为最少需要的时间。

说明/提示

对于 30% 的数据,1≤n≤200

对于 100%的数据,1≤n≤10^3, 1≤m≤10^5

1≤u,v≤n 1≤w≤10^4,输入保证任意两点都能互相到达。

分析过程:

1.首先考虑数据结构,这里每两个结点之间显然是有向边存储,结点直接也可以无边,可以采用邻接表实现:

struct edge{
	int to, w;
	edge* next;
}; 
struct fnode{
	edge *start;
};
fnode LG[1001];

for(int i = 0; i < m; i++){
	int a, b, c;
	scanf("%d%d%d", &a, &b, &c);
	edge *e = new edge;
	e->to = b;
	e->w = c;
	e->next = LG[a].start;
	LG[a].start = e;
}

2.然后考虑算法,最短路问题解法有三,迪杰斯特拉算法(堆优化),贝尔曼福德算法(优化spfa)以及弗洛伊德算法;

3.这里无需考虑负边,故舍弃第二个算法;

4.分析题目可知,求的是从一个点到多个点,以及多个点到一个点的最短路径,如果采用弗洛伊德算法,求出来很多不必要的结果,无疑增加了复杂度,所以可以舍弃第三个算法;

5.现在我们考虑采用迪杰斯特拉算法,思路大致就是从结点1出发一次迪杰斯特拉解决,再分别从各节点采用n-1次迪杰斯特拉,这样最终其实算法复杂度与弗洛伊德算法大致相同,但是由于可以进行优化,所以选择迪杰斯特拉算法。

代码实现:

迪杰斯特拉模板:

void shortest(int s){
	memset(length, inf, sizeof(length));
	memset(visited, false, sizeof(visited));
	length[s] = 0;
	for(int i = 1; i <= n; i++){
		long long minl = inf;
		int k;
		for(int j = 1; j <= n; j++){
			if(visited[j] != true && length[j] < minl){
				minl = length[j];
				k = j;
			}
		}
		visited[k] = true;
		edge *e = new edge;
		e = LG[k].start;
		while(e != NULL){
			int u = e->to;
			if(visited[u] != true && length[u] > length[k] + e->w){
				length[u] = length[k] + e->w;
			}
			e = e->next;
		}
	}
	return;
}

计数:

	shortest(1);
	for(int i = 2; i <= n; i++)	least_t += length[i];
	for(int i = 2; i <= n; i++){
		shortest(i);
		least_t += length[1];
	}

结果只有40分,显然题目卡数据了

6.所以再无脑采用堆优化:

typedef pair<int, int> P;
void shortest_update(int s){
	memset(length, inf, sizeof(length));
	length[s] = 0;
	priority_queue<P, vector<P>, greater<P> > Q;
	P p_s(0, s);
	Q.push(p_s);
	while(!Q.empty()){
		P p = Q.top();
		Q.pop();
		if(p.first > length[p.second])	continue;
		edge *e = new edge;
		e = LG[p.second].start;
		while(e != NULL){
			int u = e->to;
			if(length[u] > e->w + p.first){
				length[u] = e->w + p.first;
				P p_t(length[u], u);
				Q.push(p_t);
			}
			e = e->next;
		}
	}
	return;
}

可惜,只有60分,虽然成功提高了20分,看来无脑解题失败了

7.思路拓展,仔细想想,我们在多对一的解决方面处理的不是很好,因为这的的确确多算了许多不必要的数据。基于此点,稍加思考,在无向图的情况下,从y点到x点的最短路径,就等于x到y的最短路径,而在有向图下,由于这每一个最短路径都是存在的且固定的,可以将最短路径上的每一条边都取反,即从y点到x点的最短路径,就等于将将该图取反后x到y的最短路径。

这里在不采用堆优化的情况下也AC了!

代码实现:

fnode LG1[1001];
fnode LG2[1001];//反图 
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++){
	int a, b, c;
	scanf("%d%d%d", &a, &b, &c);
	
	edge *e1 = new edge;
	e1->to = b;
	e1->w = c;
	e1->next = LG1[a].start;
	LG1[a].start = e1;
	
	edge *e2 = new edge;
	e2->to = a;
	e2->w = c;
	e2->next = LG2[b].start;
	LG2[b].start = e2; 
}
shortest(1, 1);
for(int i = 2; i <= n; i++)	least_t += length[i];
shortest(1, 2);
for(int i = 2; i <= n; i++)	least_t += length[i];
printf("%lld", least_t);
void shortest(int s, int flag){
	......
	if(flag == 1)	e = LG1[k].start;
	else if(flag == 2) e = LG2[k].start;
	......
}

多对一问题,逆向思维,反向建图,轻松解决。

over

以上是关于最短路问题的思路拓展的主要内容,如果未能解决你的问题,请参考以下文章

51nod 1459 迷宫游戏最短路拓展

最短路思路

最短路 || Codeforces 938D Buy a Ticket

poj-2253_最短路练习

迷宫问题——最短路

poj3268