ACM:最短路,dijkstra,邻接表的建立,使用邻接表跟优先队列的dijkstra,Bellman-Ford,Floyd。。

(一)dijkstra,邻接矩阵

所有边权均为正,不管有没有环,求单个源点出发,到所有节点的最短路。该方法同时适用于有向图和无向图。

#include <iostream>
#include <string>
#include <stack>
using namespace std;

const int MAXN = 1000;
const int INF = 100000000;
int n, m;
int maze[MAXN][MAXN], vis[MAXN], d[MAXN], fa[MAXN];  //d[i]表示节点i到源点0的距离

stack<int> s;
void print_path1(int j) {
	if(j == 0) return ;
	s.push(j);
	while(j) {
		for(int i = 0; i < n; ++i) {
			if(d[j] == d[i] + maze[i][j]) {
				s.push(i);
				j = i;
				break;
			}
		}
	}
	cout << s.top();
	s.pop();
	while(!s.empty()) {
		cout << "->" << s.top();
		s.pop();
	}
	cout << endl;
}

void print_path2(int j) {
	if(j == 0) return ;
	while(j) {
		s.push(j);
		j = fa[j];
	}
	s.push(j);
	cout << s.top();
	s.pop();
	while(!s.empty()) {
		cout << "->" << s.top();
		s.pop();
	}
	cout << endl;
}

int main() {
	freopen("E://data.txt", "r", stdin);
	cin >> n >> m;
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			maze[i][j] = INF;
		}
	}
	for(int i = 0; i < m; ++i) {
		int u, v, w;
		cin >> u >> v >> w;
		maze[u][v] = maze[v][u] = w;
	}
	memset(vis, 0, sizeof(vis));
	for(int i = 0; i < n; ++i) d[i] = (i == 0 ? 0 : INF);  //初始化d数组
	for(int i = 0; i < n; ++i) {     //循环n次
		int m = INF, x;
		for(int y = 0; y < n; ++y) {     //在所有未标号的节点中,选出d值最小的节点x
			if(!vis[y] && d[y] <= m) m = d[x=y];
		}
		vis[x] = 1;
		for(int y = 0; y < n; ++y) {   //对于从x出发的所有边(x, y),更新d[y] = min(d[y], d[x]+maze[x][y])
			if(d[y] > d[x] + maze[x][y]) {
				d[y] = d[x] + maze[x][y];
				fa[y] = x;   //维护父亲指针
			}
		}
	}
	for(int i = 0; i < n; ++i) {
		cout << d[i] << endl;
		print_path1(i);  //打印路径方法1:从终点出发,不断顺着d[j] == d[i] + maze[i][j]的边(i, j)从节点j退回到节点i,直到回到起点。
		print_path2(i);  //打印路径方法2:空间换时间!在更新d数组的时候维护父亲指针!
	}
	return 0;
}

(二)邻接表的建立

邻接表既可以用于有向图也可以用于无向图,在这种表示方法中,每个节点i都有一个链表,里面保存着从i出发的所有边,对于无向图来说,每条边会在邻接表中出现两次。

我们这里用数组实现链表:首先给每条边编号,然后用first[u]保存节点u的第一条边的编号,next[e]表示编号为e的边的“下一条边”的编号。

下面的代码针对有向图,建立邻接表。

#include <iostream>
using namespace std;

const int MAXN = 1000;
int first[MAXN], next[MAXN], u[MAXN], v[MAXN], w[MAXN];

int main() {
	int n, m;
	cin >> n >> m;
	for(int i = 0; i < n; ++i) first[i] = -1;
	for(int e = 0; e < m; ++e) {
		cin >> u[e] >> v[e] >> w[e];
		next[e] = first[u[e]];
		first[u[e]] = e;
	}
	for(int i = 0; i < n; ++i) cout << first[i] << endl;
	for(int e = 0; e < m; ++e) cout << next[e] << endl;
	return 0;
}

上述代码的巧妙之处是插入到链表的首部而非尾部,这样就避免了对链表的遍历。在这里,同一个起点的各条边在邻接表中的顺序和读入顺序正好相反。

(三)使用邻接表跟优先队列的dijkstra。

queue跟priority_queue的唯一区别是,在优先队列中,元素并不是按照进入队列的先后顺序排列,而是按照优先级的高低顺序排列。pop()删除的是优先级最高的元素,而不一定是最先进入队列的元素。所以,获取对首元素的方法不再是front(),而是top()。

struct cmp{
	bool operator() (const int a, const int b) {  //a的优先级比b小时返回true
		return a % 10 > b % 10;
	}
};
priority_queue<int, vector<int>, cmp> q;  //“个位数大的优先级反而小”的整数优先队列

声明一个小整数先出队列的优先队列:

priority_queue< int, vector<int>, greater<int> > q;

在dijkstra算法中,不仅需要找出最小的d[i],要连同这个节点的编号一起从优先队列中弹出来,所以我们用pair

为了方便起见,我们用typedef pair<int, int> pii自定义一个pii类型,则priority_queue< pii, vector<pii>, greater<pii> > q 就定义了一个由二元组构成的优先队列!

pair定义了它自己的排序规则——先比较第一维,相等时才比较第二维,因此需要按(d[i], i)而不是(i, d[i]) 的方式组合!

利用邻接表+二叉堆来实现dijkstra算法的代码如下:

#include <iostream>
#include <queue>
using namespace std;

const int MAXN = 1000;
const int MAXM = 100000;
const int INF = 100000000;
int n, m;
int first[MAXN], d[MAXN], done[MAXN];   //在寻找距离源点最近的点x过程中,done[i]表示第i个节点已经被处理过
int u[MAXM], v[MAXM], w[MAXM], next[MAXM];
typedef pair<int, int> pii;

int main() {
	cin >> n >> m;
	for(int i = 0; i < n; i++) first[i] = -1;  //初始化邻接表的表头
	for(int e = 0; e < m; ++e) {     //邻接表的建立
		cin >> u[e] >> v[e] >> w[e];
		next[e] = first[u[e]];
		first[u[e]] = e;
	}
	priority_queue< pii, vector<pii>, greater<pii> > q;  //用于在所有未处理过的节点中,选出d值最小的节点x
	memset(done, 0, sizeof(done));  //一开始假设所有节点都没有被处理过
	q.push(make_pair(d[0], 0));   //起点进入优先队列
	for(int i = 0; i < n; ++i) d[i] = (i == 0 ? 0 : INF);
	while(!q.empty()) {
		pii u = q.top();
		q.pop();
		int x = u.second;   //x表示当前d值最小的节点的节点号
		if(done[x]) continue;    //已经算过,忽略
		done[x] = 1;
		for(int e = first[x]; e != -1; e = next[e]) {   //遍历从x出发的所有边(x,y)更新d[y]
			if(d[v[e]] > d[x] + w[e]) {
				d[v[e]] = d[x] + w[e];      //松弛成功,更新d[v[e]]
				q.push(make_pair(d[v[e]], v[e]));
			}
		}
	}
	for(int i = 0; i < n; ++i) cout << d[i] << endl;
	return 0;
}

(四)Bellman-Ford算法

当图中有负权的时候,最短路就不一定存在了,但是还是可以在最短路存在的情况下把它求出来。

如果最短路存在,则该最短路一定不含环!

原因:分为正环,零环,负环三种情况考虑!

如果是正环或零环,那最短路肯定不经过它!如果是负环,那肯定就不存在最短路了!

既然最短路不含环,那么该最短路就最多只经过n-1个节点(起点不算),所以可以通过n-1轮松弛操作得到!

#include <iostream>
using namespace std;

const int MAXN = 1000;
const int INF = 100000000;
int n, m;
int d[MAXN], u[MAXN], v[MAXN], w[MAXN];

int main() {
	cin >> n >> m;
	for(int e = 0; e < m; ++e) {
		cin >> u[e] >> v[e] >> w[e];
	}
	for(int i = 0; i < n; ++i) d[i] = INF;
	d[0] = 0;
	for(int k = 0; k < n-1; ++k) {   //迭代n-1次
		for(int e = 0; e < m; ++e) {   //检查每条边
			int x = u[e];
			int y = v[e];
			if(d[x] < INF) d[y] = min(d[y], d[x] + w[e]);  //松弛操作
		}
	}
	for(int i = 0; i < n; ++i) cout << d[i] << endl;
	return 0;
}

用队列实现的话,效率会更高,像这样:

#include<iostream>
#include<string>
#include<queue>
using namespace std;

const int INF = 1000000000;
const int MAXN = 1000;
const int MAXM = 100000;

int n, m;
int first[MAXN], d[MAXN];
int u[MAXM], v[MAXM], w[MAXM], next[MAXM];

int main() {
	cin >> n >> m;
	for(int i = 0; i < n; ++i) first[i] = -1;
	for(int e = 0; e < m; ++e) {
		cin >> u[e] >> v[e] >> w[e];
		next[e] = first[u[e]];
		first[u[e]] = e;
	}
	queue<int> q;
	int inq[MAXN];
	for(int i = 0; i < n; ++i) d[i] = (i==0 ? 0 : INF);
	memset(inq, 0, sizeof(inq));
	q.push(0);
	while(!q.empty()) {
		int x = q.front(); q.pop();
		inq[x] = 0;    //标记x不在队列中
		for(int e = first[x]; e != -1; e = next[e]) if(d[v[e]] > d[x]+w[e]) {
			d[v[e]] = d[x] + w[e];
			if(!inq[v[e]]) {    //如果点v[e]不在队列中
				inq[v[e]] = 1;   //标记点v[e]在队列中
				q.push(v[e]);
			}
		}
	}
	for(int i = 0; i < n; i++)	cout << d[i] << endl;
	return 0;
}

(五)Floyd算法

如果要求计算每两点之间的最短路:

#include <iostream>
using namespace std;

const int MAXN = 1000;
const int INF = 1000000;
int d[MAXN][MAXN];
int n;

int main() {
	cin >> n;
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			if(i == j) d[i][j] = 0;
			else d[i][j] = INF;
		}
	}
	for(int k = 0; k < n; ++k) {
		for(int i = 0; i < n; ++i) {
			for(int j = 0; j < n; ++j) {
				if(d[i][j] < INF && d[k][j] < INF) d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
			}
		}
	}
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			cout << d[i][j] << endl;
		}
	}
	return 0;
}

ACM:最短路,dijkstra,邻接表的建立,使用邻接表跟优先队列的dijkstra,Bellman-Ford,Floyd。。

时间: 2024-12-09 12:01:55

ACM:最短路,dijkstra,邻接表的建立,使用邻接表跟优先队列的dijkstra,Bellman-Ford,Floyd。。的相关文章

交易事实表、周期快照事实表和累积快照事实表

在数据仓库领域有一个概念叫Transaction fact table,中文一般翻译为"事务事实表".事务事实表是维度建模的数据仓库中三种基本类型事实表中的一种,另外两种分别是周期快照事实表和累积快照事实表. 事务事实表与周期快照事实表.累积快照事实表使用相同的一致性维度,但是它们在描述业务事实方面是有着非常大的差异的. 事务事实表记录的事务层面的事实,保存的是最原子的数据,也称"原子事实表".事务事实表中的数据在事务事件发生后产生,数据的粒度通常是每个事务一条记录

[ACM] 最短路算法整理(bellman_ford , SPFA , floyed , dijkstra 思想,步骤及模板)

以杭电2544题目为例 最短路 Problem Description 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗? Input 输入包括多组数据.每组数据第一行是两个整数N.M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路.N=M=0

水电费管理系统需求分析----表格的建立(&#160;电费表和价格表有待完善......)

--建立用户信息表 create table users ( users_id varchar2(12) not null, users_name varchar2(12) not null, users_username varchar2(6) not null, users_userpassword varchar2(6) not null, users_phone number(11) not null, users_address varchar2(60) not null ) --添加

使用MySQL Workbench建立数据库,建立新的表,向表中添加数据

初学数据库,记录一下所学的知识.我用的MySQL数据库,使用MySQL Workbench管理.下面简单介绍一下如何使用MySQL Workbench建立数据库,建立新的表,为表添加数据. 点击上图中的“加号”图标,新建一个连接, 如上图,先输入数据库的账号密码,帐号默认为root,填好密码后 点击“OK”,连接就建立好了,建立完成后,会出现一个长方形的框框,双击它,出现下图所示页面 点击图中的红圈里的按钮,新建一个Schema,即数据库(个人理解..),修改一下Name 的值,如  mydat

js表单建立必填字段

在填写表单时可能希望用户必须填写某些字段或者两段填写的字段相匹配,然后才能提交表单,这里就可以用js来实现 下面是建立一个基础表单的HTML代码 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset = "utf-8"> 5 <title>Password check</title> 6 <link rel="stylesheet" typ

跟我一起学extjs5(28--加入模块和菜单定义[1建立数据库和表])

跟我一起学extjs5(28--加入模块和菜单定义[1建立数据库和表]) 下面我们开始进入自定义系统的设计和各个组成部分的搭建工作. 首先在sql server中建立数据库,这里的数据的名字我定义为extjs5,然后需要建立模块的定义表,这个表里将存着系统中所有模块的定义信息.为了能有个直观的前后台的互动,先把模块和菜单这二个部分加进去,可以把菜单数据组织好后发送到前台去进行展示. 由于系统中的模块很多,需要分类,因此在先要设计一个"模块分组"的表,对于菜单也是一样,需要有一个&quo

26、蛤蟆的数据结构笔记之二十六串应用之建立词索引表

26.蛤蟆的数据结构笔记之二十六串应用之建立词索引表 本篇名言:"生命是一条美丽而曲折的幽径,路旁有妍花的丽蝶,累累的美果,但我们很少去停留观赏,或咀嚼它,只一心一意地渴望赶到我们幻想中更加美丽的豁然开朗的大道.然而在前进的程途中,却逐渐树影凄凉,花蝶匿迹,果实无存,最后终于发觉到达一个荒 漠.-- 萨拉" 1.  信息检索 信息检索是计算机应用的重要领域之一.为了提高图书馆数目检索的效率,建立书名关键词索引,可以实现读者快速检索书目的自动化,即读者根据关键词索引表,读者可以方便查询到

hive外部表的建立与数据匹配

1.建立hive的外部表匹配hdfs上的数据 出现如下报错: hive (solar)> select * from solar.ori_mysql_sqoop_open_third_party_user_da limit 10; OK Failed with exception java.io.IOException:java.io.IOException: Not a file: hdfs://f04/sqoop/open/third_party_user/dt=2016-12-12 Tim

顺序查找,折半查找,二叉排序树的建立,哈希表的建立

以下四个验证性实验都做. (1)顺序查找验证 (2)折半查找验证 (3)二叉排序树的建立 (4)哈希表的建立 #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<string.h> #include<algorithm> using namespace std; class dijiuzhang { public: int a[1