迪杰斯特拉算法应用——PAT A1003 Emergency

《算法笔记》P378
emmm感觉从迪杰斯特拉开始,脑子就不够用了= =
输入样例:
第一行:顶点数、边数、起始顶点、结束顶点

5 6 0 2
第二行:每个顶点的点权(第几就是顶点几)
1 2 1 5 3
第3-3+边数行:两两顶点的边权
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
输出:起点到终点的最短路径条数、最短路径上的救援小组数目之后(如果有多条最短路径,则输出数目之和最大的)
2 4

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

const int maxn = 510;
const int INF = 100000000;
//n为顶点数,m为边数,st、ed为起点、终点
//G为存储图的邻接矩阵,weight为点权 
int n,m,st,ed,G[maxn][maxn],weight[maxn];
//d记录最短距离,w记录最大点权数之和,num记录最短路径条数 
int d[maxn],w[maxn],num[maxn];
bool vis[maxn] = {false};

void Dijkstra(int s){
	fill(d,d+maxn,INF);
	memset(num,0,sizeof(num));
	memset(w,0,sizeof(w));
	d[s] = 0;
	w[s] = weight[s];
	num[s] = 1;
	for(int i = 0;i <n;i++){
		int u = -1,MIN = INF;
		for(int j = 0;j <n;j++){
			if(vis[j] == false && d[j]<MIN){
				u = j;
				MIN = d[j];
			}
		}
		if(u == -1) return;
		vis[u] = true;
		for(int v = 0;v <n;v++){    
			//如果v未访问 && u能到达v 
			if(vis[v] == false && G[u][v] != INF){
				//&& 以u为中介能使d[v]更优 
				if(d[u]+G[u][v] <d[v]){       
					d[v] = d[u] + G[u][v];     //覆盖d[v]
					w[v] = w[u] + weight[v];   //覆盖w[v]
					num[v] = num[u];          //覆盖num[v]
				}
				else if(d[u] + G[u][v] == d[v]){    //找到一条相同长度的路径 
					if(w[u] + weight[v] >w[v]){     //以u为中介点时点权之和更大 
						w[v] = w[u] + weight[v];    //w[v]继承自w[u] 
					}
					num[v] = num[v] + num[u];     //最短路径条数与点权无关,必须写在外面 
				}
			}
		}
	}
}

int main(){
	scanf("%d%d%d%d",&n,&m,&st,&ed);
	for(int i = 0;i <n;i++){
		scanf("%d",&weight[i]);
	}
	int u,v;
	fill(G[0],G[0]+maxn*maxn,INF);
	for(int i = 0;i <m;i++){
		scanf("%d%d",&u,&v);
		scanf("%d",&G[u][v]);
		G[v][u] = G[u][v];
	}
	Dijkstra(st);
	printf("%d %d\n",num[ed],w[ed]);
	return 0;
}

/*
5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

*/


若在以上问题基础上,要求输出最短路径的前驱节点如何处理?
(迪杰斯特拉部分代码根据书上写的,DFS部分自己写的,但似乎对于多个最短路径的时候遍历并不好。。)
代码:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn = 510;
const int INF = 1000000000;
vector<int> pre[maxn];
int n,m,st,ed,G[maxn][maxn],weight[maxn];
int d[maxn];
bool vis[maxn] = {false};

void Dijsktra(int s){
	fill(d,d+maxn,INF);
	d[s] = 0;
	for(int i = 0;i <n;i++){
		int u = -1,MIN = INF;
		for(int j = 0;j <n;j++){
			if(vis[j] == false && d[j] <MIN){
				u = j;
				MIN = d[j];
			}
		}
		if(u == -1) return;
		vis[u] = true;
		for(int v = 0;v <n;v++){
			if(vis[v] == false && G[u][v] != INF){
				if(d[u] + G[u][v] <d[v]){
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if(d[u] + G[u][v] == d[v]){
					pre[v].push_back(u);
				}
			}
		}
	}
}
void DFS(int s){
	if(s == st){
		for(int i = 0;i <pre[st].size();i++){
			printf("%d ",pre[st][i]);
		}
	}
	else{
		for(int j = 0;j <pre[s].size();j++){
			printf("%d ",pre[s][j]);
			DFS(pre[s][j]);

		}
	}
}


int main(){
	scanf("%d %d %d %d",&n,&m,&st,&ed);
	for(int i = 0;i <n;i++){
		scanf("%d",&weight[i]);
	}
	int u,v;
	fill(G[0],G[0]+maxn*maxn,INF);
	for(int i = 0;i <m;i++){
		scanf("%d %d",&u,&v);
		scanf("%d",&G[u][v]);
		G[v][u] = G[u][v];
	}
	Dijsktra(st);
	for(int i = 1;i <n;i++){
		printf("%d\n",d[i]);   //每个顶点的最短距离 
	}
	DFS(ed);	//输出到终点最短路径前驱的顶点 
}
/*
5 6 0 4
1 2 1 5 3
0 1 1
0 2 4
0 3 5
1 2 1
2 4 1
3 4 1

*/


《算法笔记》P384,对于有第二标尺的情况下,DFS选择第二标尺最优的路径来输出:
伪代码如下:

int optvalue;             //第二标尺最优值 
vector<int>path,tempath;  //存放最优路径,临时路径 
void DFS(int v){
	//递归边界
	if(v == st){
		tempath.push_back(v);
		int value;
		计算路径tempath上的value值;
		if(value优于optvalue){
			optvalue = value;
			path = tempath;
		}
		tempath.pop_back();
		return; 
	}
	tempath.push_back(v);
	for(int i = 0;i <pre[v].size();i++){
		DFS(pre[v][i]);     //节点v的前驱节点pre[v][i]递归 
	}
	tempath.pop_back();    //遍历完所有前驱节点,将当前节点v删除 
}

如下是完整实现代码(末行输出最短路径上的顶点,倒序输出,是从起点到终点的顺序;并且有相同最短路径时,根据第二标尺点权之和最优来输出)
有几点要注意:
1、path可变数组、tempath临时可变数组
2、由于递归的原因,存放在tempath中的路径节点是逆序的,因此访问节点需要倒着进行(这也是DFS之中和最后输出的时候倒序访问的原因)

代码:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn = 510;
const int INF = 1000000000;
vector<int> pre[maxn];
int n,m,st,ed,G[maxn][maxn],weight[maxn];
int d[maxn];
bool vis[maxn] = {false};
int optvalue = 0;             //第二标尺最优值 
vector<int>path,tempath;  //存放最优路径,临时路径 

void Dijsktra(int s){
	fill(d,d+maxn,INF);
	d[s] = 0;
	for(int i = 0;i <n;i++){
		int u = -1,MIN = INF;
		for(int j = 0;j <n;j++){
			if(vis[j] == false && d[j] <MIN){
				u = j;
				MIN = d[j];
			}
		}
		if(u == -1) return;
		vis[u] = true;
		for(int v = 0;v <n;v++){
			if(vis[v] == false && G[u][v] != INF){
				if(d[u] + G[u][v] <d[v]){
					d[v] = d[u] + G[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if(d[u] + G[u][v] == d[v]){
					pre[v].push_back(u);
				}
			}
		}
	}
}

void DFS(int v){
	//递归边界
	if(v == st){
		tempath.push_back(v);
		/*  边权之和 
		int value = 0;
		for(int i = tempath.size() - 1;i >0;i--){
			int id = tempath[i],idNEXT = tempath[i-1];
			value += V[id][idNEXT];
		}
		
		*/
		
		//点权之和 
		int value = 0;
		for(int i = tempath.size() - 1;i>= 0;i--){
			int id = tempath[i];
			value += weight[id];
		} 
		if(value>optvalue){
			optvalue = value;
			path = tempath;
		}
		tempath.pop_back();
		return; 
	}
	tempath.push_back(v);
	for(int i = 0;i <pre[v].size();i++){
		DFS(pre[v][i]);     //节点v的前驱节点pre[v][i]递归 
	}
	tempath.pop_back();    //遍历完所有前驱节点,将当前节点v删除 
}


int main(){
	scanf("%d %d %d %d",&n,&m,&st,&ed);
	for(int i = 0;i <n;i++){
		scanf("%d",&weight[i]);
	}
	int u,v;
	fill(G[0],G[0]+maxn*maxn,INF);
	for(int i = 0;i <m;i++){
		scanf("%d %d",&u,&v);
		scanf("%d",&G[u][v]);
		G[v][u] = G[u][v];
	}
	Dijsktra(st);
	for(int i = 1;i <n;i++){
		printf("%d\n",d[i]);   //每个顶点的最短距离 
	}
	DFS(ed);	//输出到终点最短路径前驱的顶点 
	for(int i = path.size() - 1;i >= 0;i--){   //倒序输出路径上的点 
		printf("%d ",path[i]);
	}
}
/*
5 6 0 4
1 2 1 5 3
0 1 1
0 2 4
0 3 5
1 2 1
2 4 1
3 4 1

*/

发布了212 篇原创文章 · 获赞 6 · 访问量 6413

猜你喜欢

转载自blog.csdn.net/weixin_42377217/article/details/104249760
今日推荐