Poj3255 Roadblocks SPFA算法解“次短路”问题 代码&注释

题目大意:给定一个无向图,图顶点数N≤5000,边数R≤100000,求顶点1到顶点N的“次短路”。

注意:

  1. 多次经过同一顶点或同一条边的路径是被允许的(允许有环的路径存在)。
  2. “次短路“指的是数值上的次短,例如,顶点1到N有两条最短路径,长度均为100,还有一条路径长为101,则次短路长度应输出101,而非100。
// Poj 3255 Roadblocks
// By Victor Li
// 2016-09-03

// SPFA算法解次短路问题
// SPFA算法不一定局限于求解最短路问题,图中的有状态表示和转移的最优化问题都可以考虑应用SPFA算法的变种来处理
// SPFA算法适用于稀疏图

#include <stdio.h>
#include <stdlib.h>
#include <queue>
using namespace std;

const int oo = 5*10e8;

struct enode{

	int v;
	int d;
	enode * next;
};

void addEdge(int x, int y, int d, enode **e, enode **en){

	if (en[x] == 0){

		e[x] = new enode;
		e[x]->v = y;
		e[x]->d = d;
		e[x]->next = NULL;
		en[x] = e[x];
	}	else {

		en[x]->next = new enode;
		en[x]->next->v = y;
		en[x]->next->d = d;
		en[x]->next->next = NULL;
		en[x] = en[x]->next;
	}
}

//松弛操作
int relax(int x, int y, int d, int * dist){

	if (dist[x] + d < dist[y]){
		int tmp = dist[y];
		dist[y] = dist[x] + d;
		return tmp;
	}	else 
		return -1;
}

int main(){

	int n, m, x, y, d, tmp;
	scanf("%d%d",&n,&m);

	//用链表存储每个顶点连接的所有边的信息;e[i]为顶点i对应的边链表的表头
	//为了提高构建边链表的效率,用en[i]存储顶点i对应的边链表的末尾
	enode ** e  = (enode **) calloc(n, sizeof(enode *));
	enode ** en = (enode **) calloc(n, sizeof(enode *));
	for (int i=0;i<m;i++){

		scanf("%d%d%d",&x,&y,&d); --x; --y;
		addEdge(x,y,d,e,en);
		addEdge(y,x,d,e,en);
	}

	//0 =< i < n   : dist[i] 从顶点0到顶点i的最短路长度
	//n =< i < 2*n : dist[i] 从顶点0到顶点i-n的次短路的长度
	int  * dist = (int  *) calloc(2*n, sizeof(int ));
	for (int i=1;i<2*n;i++) dist[i] = oo;
	bool * flag = (bool *) calloc(2*n, sizeof(bool));
	flag[0] = true;

	//应用SPFA算法求解
	queue<int> Q;
	Q.push(0);
	while (!Q.empty()){

		x = Q.front();	
		Q.pop();
		flag[x] = false;

		if (x<n) {
			
			enode * et = e[x];
			while (et!=NULL){

				//这里使用tmp记录下了“松弛”过程中被更短的路径顶掉的之前的最短路径的长度
				//记录下它是因为它有可能是到达该顶点的“次短路”,可以用来更新dist[et->v+n]
				if ( (tmp = relax(x,et->v,et->d,dist))>-1 ){

					if (!flag[et->v]){

						Q.push(et->v);
						flag[et->v] = true;
					}
					if ( tmp<dist[et->v+n]){

						dist[et->v+n] = tmp;
						if (!flag[et->v+n]){

							Q.push(et->v+n);
							flag[et->v+n] = true;
						}
					}
					
				} 	else {

					//当前路径长度更新不了到达该节点的最短路径,则再尝试更新次短路径
					if ( dist[x]+et->d>dist[et->v] && relax(x,et->v+n,et->d,dist)>-1 )
						if (!flag[et->v+n]){

							Q.push(et->v+n);
							flag[et->v+n] = true;
						}

				}

				et = et->next;
			}
		}	else {

			//x>=n,说明当前的队头对应的是一条“次短路径”
			//次短路径不可能经过一条边到达下一个顶点时就成为到达下一顶点的最短路径
			//因此这里只考虑更新下一顶点的次短路径的情况
			enode * et = e[x-n];
			while (et!=NULL){

				if (relax(x,et->v+n,et->d,dist)>-1)
					if (!flag[et->v+n]){

						Q.push(et->v+n);
						flag[et->v+n] = true;
					}

				et = et->next;
			}
		}
	}

	//该算法可以求出到达图中每一节点的最短路径和次短路径
	printf("%d\n",dist[2*n-1]);

	return 0; 
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值