dijkstra入门

dijkstra算法是一个求从源点到图上所有点最短路径的一种算法

注意事项:如果权值为负的是不能用dijkstra算法的,因为假设存在一个回路,回路上的和小于0,则一直走这个回路,最短路径会变成无穷小

过程:1.首先定义一个数组int型dis[maxN],dis[i]代表从源点到i的最短路径,然后全部赋值为inf(无穷大,或者一个很大的数字)

定义一个定义bool数组visit[maxN],visit[i]为false代表没有访问过i,为true代表访问过i

定义一个邻接矩阵map[maxN][maxN],map[i][j]代表i到j的代价为map[i][j],如果为inf则没有路

假设起点为s,令dis[s]=0

2.找到dis数组中最小的且没有访问过的点pos,令visit[pos]=true,代表pos已经被访问过了

3.松弛,遍历数组dis,对于没有访问过的点i,dis[i]=min(dis[i],dis[pos]+map[pos][i]),相当于从源点到i,是直接过去短,还是经过pos点在过去短(对于访问过的点,是不存在更短的路径的,因为每次都是取最短的,比如依次访问i,j,每次都是取最短,则dis[i]<dis[j],min(dis[i],dis[j]+map[i][j])=dis[i],所以不存在更短的)

4.重复2,3,直到所有的点都被访问过了

注意事项:如果权值为负的是不能用dijkstra算法的,因为假设存在一个回路,回路上的和小于0,则一直走这个回路,最短路径会变成无穷小

#include<iostream>
#include<cstring>
#define min(a,b) (a<b?a:b)//两者较小的
const int maxN = 10000, inf = 0x3f3f3f3f;
bool visit[maxN];
int dis[maxN], map[maxN][maxN], n;
void dijkstra(const int &s,const int &t) {
	//visit[i]为i是否被访问过,一开始都没访问过,所以都为false
	memset(visit, false, sizeof(visit));
	//dis[i]为源点s到i的最短路径,一开始都为inf
	memset(dis, inf, sizeof(dis));
	//起点到起点显然路径为0
	dis[s] = 0;
	int pos;
	for (int i = 1; i <= n; ++i) {
		pos = -1;
		//需找最小的dis,pos赋值为-1是为了找到第一个
		for (int j = 1; j <= n; ++j)
			if (!visit[j] && (pos == -1 || dis[pos] < dis[j]))pos = j;
		//如果是-1说明都访问过了
		if (pos == -1)break;
		//如果终点是t,就说明已经到终点了,如果需要遍历图的话,下面就不用写了
		if (pos == t)break;
		//松弛
		for (int j = 1; j <= n; ++j)
			if (!visit[j])dis[j] = min(dis[j], dis[pos] + map[pos][j]);
	}
	//如果dis[t]=inf说明没有路到达t,否则最短路径为dis[t]
	if (dis[t] == inf)printf("-1");
	else printf("%d\n", dis[t]);
}
int main() {
	int  m, a, b, c;
	scanf("%d%d", &n, &m);
	//map为邻接矩阵,先假设都没有路,即为inf
	memset(map, inf, sizeof(map));
	while (m--) {
		//输入a,b,c,代表a到b有一条代价为c的路
		scanf("%d%d%d", &a, &b, &c);
		//因为是双向边,所以map[a][b]=map[b][a],但是如果题目没说双向边,就直接map[a][b]
		//min(map[a][b],c)是为了防止有重边,有重边时显然应该选择代价小的,但是如果题目说没有重边可以直接赋值c
		map[a][b] = map[b][a] = min(map[a][b], c);
	}
	dijkstra();
}

路径还原

#include<iostream>
#include<cstring>
#include<stack>
#define min(a,b) (a<b?a:b)
using namespace std;
const int maxN = 100, inf = 0x3f3f3f3f;
bool visit[maxN];
int path[maxN], dis[maxN], map[maxN][maxN], n;
void dijkstra() {
	memset(visit, false, sizeof(visit));
	memset(dis, inf, sizeof(dis));
	dis[1] = 0;
	int pos;
	for (int i = 1; i <= n; ++i) {
		pos = -1;
		for (int j = 1; j <= n; ++j)
			if (!visit[j] && (pos == -1 || dis[j] < dis[pos]))pos = j;
		if (pos == -1)break;
		visit[pos] = true;
		for (int j = 1; j <= n; ++j)
			if (!visit[j] && dis[j] > dis[pos] + map[pos][j]) {
				dis[j] = dis[pos] + map[pos][j];
				path[j] = pos;
			}
	}
}
void getPath() {
	stack<int> s;
	int t = n;
	for (; t != -1; t = path[t])s.push(t);
	while (!s.empty()) {
		printf("%d ", s.top());
		if (s.top() != n)printf("->");
		s.pop();
	}
	printf("\n");
}
int main() {
	int m, a, b, c;
	scanf("%d%d", &n, &m);
	memset(map, inf, sizeof(map));
	while (m--) {
		scanf("%d%d%d", &a, &b, &c);
		map[a][b] = map[b][a] = min(map[a][b], c);
	}
	memset(path, -1, sizeof(path));
	dijkstra();
	printf("最短路径距离:%d\n", dis[n]);
	printf("最短路径:");
	getPath();
	return 0;
}

显然算法时间时间复杂度o(n^2)

当然,可以进行优先队列优化,队列优化的可以不用管重边

具体操作就是用邻接表储存,然后定义一个优先队列,把起点的信息入队,然后每次队列取代价最小的点,由这个点遍历所有可以访问的点然后循环

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxN = 10001, inf = 0x3f3f3f3f;
vector<pair<int, int> > edge[maxN];//邻接表,pair第一个代表点,第二个代表代价,注意千万别写pair<int,int>>,最后两>一定要有一个空格,不然就认为是位运算右移了
int n, dis[maxN];
void init() {
	for (int i = 1; i <= n; ++i) {
		edge[i].clear();//清空邻接表
		dis[i] = inf;
	}
}
void dijkstra(const int &s, const int &t) {
	dis[s] = 0;
	priority_queue<pair<int, int> > q;//优先队列,pair第一个是代价,第二个是点
									  //优先队列默认是从大到小的,pair的比较规则是先比第一个,再比第二个,所以dis[s]加一个负号就相当于从小到大了,由于优先队列只是为了取出点,所以第一个元素是什么都无所谓
									  //制造一个pair用的是make_pair
	q.push(make_pair(-dis[s], s));
	while (!q.empty()) {
		int now = q.top().second;
		//找到终点了就终止循环,如果要遍历就不用写这句了
		if (now == t)break;
		q.pop();
		for (int i = 0; i < edge[now].size(); ++i) {
			int v = edge[now][i].first;//取出到达的点
									   //松弛
			if (dis[v] > dis[now] + edge[now][i].second) {
				dis[v] = dis[now] + edge[now][i].second;
				q.push(make_pair(-dis[v], v));//入队
			}
		}
	}
	//如果dis[t]=inf说明没有路到达t,否则最短路径为dis[t]
	if (dis[t] == inf)printf("-1");
	else printf("%d\n", dis[t]);
}
int main() {
	int m, a, b, c;
	init();
	while (m--) {
		scanf("%d%d%d", &a, &b, &c);
		//邻接表,代表a到b有一条价值为c的边
		edge[a].push_back(make_pair(b, c));
		//双向边,如果不是下面这句就不用写了
		edge[b].push_back(make_pair(a, c));
	}
	dijkstra();
	return 0;
}

类的版本||结构体的版本,其实差不多

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxN = 100001, inf = 0x3f3f3f3f;
class point {
public:
	//v是到达的点,p是代价
	int v, p;
	point(const int &v, const int &p) :v(v), p(p) {}
	//优先队列一定要重载小于号
	bool operator <(const point &t)const {
		return p > t.p;
	}
};
vector<point> edge[maxN];
int dis[maxN], n;
void init() {
	for (int i = 1; i <= n; ++i) {
		edge[i].clear();
		dis[i] = inf;
	}
}
void dijkstra(const int &s,const int &t) {
	priority_queue<point> q;
	dis[s] = 0;
	q.push(point(s, dis[s]));
	while (!q.empty()) {
		int now = q.top().v;
		if (now == t)break;
		q.pop();
		for (int i = 0; i < edge[now].size(); ++i) {
			int v = edge[now][i].v;
			if (dis[v] > dis[now] + edge[now][i].p) {
				dis[v] = dis[now] + edge[now][i].p;
				q.push(point(v, dis[v]));
			}
		}
	}
	//如果dis[t]=inf说明没有路到达t,否则最短路径为dis[t]
	if (dis[t] == inf)printf("-1");
	else printf("%d\n", dis[t]);
}
int main() {
	int m, a, b, c;
	init();
	while (m--) {
		scanf("%d%d%d", &a, &b, &c);
		edge[a].push_back(point(b, c));
		//双向边
		edge[b].push_back(point(a, c));
	}
	dijkstra();
	return 0;
}

路径还原

#include<iostream>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
const int maxN = 1000, inf = 0x3f3f3f3f;
vector<pair<int, int> > edge[maxN];
int n, dis[maxN], path[maxN];
void init() {
	for (int i = 1; i <= n; ++i) {
		edge[i].clear();
		dis[i] = inf;
		path[i] = -1;
	}
}
void dijkstra() {
	priority_queue<pair<int, int> > q;
	q.push(make_pair(0, 1));
	dis[1] = 0;
	while (!q.empty()) {
		int now = q.top().second;
		if (now == n)return;
		q.pop();
		for (int i = 0; i < edge[now].size(); ++i) {
			int v = edge[now][i].first;
			if (dis[v] > dis[now] + edge[now][i].second) {
				dis[v] = dis[now] + edge[now][i].second;
				q.push(make_pair(-dis[v], v));
				path[v] = now;
			}
		}
	}
}
void getPath() {
	stack<int> s;
	int t = n;
	for (; t != -1; t = path[t])s.push(t);
	while (!s.empty()) {
		printf("%d ", s.top());
		if (s.top() != n)printf("->");
		s.pop();
	}
	printf("\n");
}
int main() {
	int m, a, b, c;
	scanf("%d%d", &n, &m);
	init();
	while (m--) {
		scanf("%d%d%d", &a, &b, &c);
		edge[a].push_back(make_pair(b, c));
		edge[b].push_back(make_pair(a, c));
	}
	dijkstra();
	printf("最短路径距离:%d\n", dis[n]);
	printf("最短路径:");
	getPath();
	return 0;
}

算法复杂度o((m+n)*log(n))

—————————————————————————-

题目

poj1502

题目大意:告诉你图的下三角,求从源点到达所有点的最短路径的最大值

解法:实际上就是裸的dijkstra

#include<iostream>
#include<cstring>
#include<stdlib.h>
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
const int maxN = 101, inf = 0x3f3f3f3f;
bool visit[maxN];
int map[maxN][maxN], dis[maxN], n;
void dijkstra() {
	memset(visit, false, sizeof(visit));
	memset(dis, inf, sizeof(dis));
	dis[1] = 0;
	int pos;
	for (int i = 1; i <= n; ++i) {
		pos = -1;
		for (int j = 1; j <= n; ++j)
			if (!visit[j] && (pos == -1 || dis[j]<dis[pos]))pos = j;
		if (pos == -1)return;
		visit[pos] = true;
		for (int j = 1; j <= n; ++j)
			if (!visit[j])dis[j] = min(dis[j], dis[pos] + map[pos][j]);
	}
}
int main() {
	char s[100];
	int ans;
	while (scanf("%d", &n) != EOF) {
		memset(map, inf, sizeof(map));
		ans = map[1][1] = 0;
		for (int i = 2; i <= n; ++i) {
			map[i][i] = 0;
			for (int j = 1; j<i; ++j) {
				scanf("%s", s);
				if (s[0] == 'x')continue;
				map[i][j] = map[j][i] = atoi(s);
			}
		}
		dijkstra();
		for (int i = 1; i <= n; ++i)ans = max(dis[i], ans);
		printf("%d\n", ans);
	}
	return 0;
}

———————————————————-

poj3268

题目大意:n个农场,选一个农场x嗨,问每个农场到x再回去,求所有最短路径的最大值

解法:单向边,从i到x走一次,x到i走一次,加起来就是一个最短路径了,然后取最大值

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#define max(a,b) (a>b?a:b)
using namespace std;
const int maxN=1001,inf=0x3f3f3f3f;
vector<pair<int,int> > edge[maxN];
int dis[maxN];
int dijkstra(const int &s,const int &e){
	memset(dis,inf,sizeof(dis));
	priority_queue<pair<int,int> > q;
	dis[s]=0;
	q.push(make_pair(dis[s],s));
	while(!q.empty()){
		int now=q.top().second;
		if(now==e)return dis[e];
		q.pop();
		for(int i=0;i<edge[now].size();++i){
			int v=edge[now][i].first;
			if(dis[v]>dis[now]+edge[now][i].second){
				dis[v]=dis[now]+edge[now][i].second;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
}
int main(){
	int n,m,x,a,b,c,ans;
	while(scanf("%d%d%d",&n,&m,&x)!=EOF){
		for(int i=1;i<n;++i)edge[i].clear();
		while(m--){
			scanf("%d%d%d",&a,&b,&c);
			edge[a].push_back(make_pair(b,c));
		}
		ans=0;
		for(int i=1;i<=n;++i)ans=max(ans,dijkstra(i,x)+dijkstra(x,i));
		printf("%d\n",ans);
	}
	return 0;
}

——————————

poj2387

题目大意:求从n到1的最短路径

解法:裸的dijkstra,两种都贴一下吧

#include<iostream>
#include<cstring>
#define min(a,b) (a<b?a:b)
const int maxN=1001,inf=0x3f3f3f3f;
bool visit[maxN];
int dis[maxN],map[maxN][maxN],n;
void dijkstra(){
	memset(visit,false,sizeof(visit));
	memset(dis,inf,sizeof(dis));
	dis[n]=0;
	int pos;
	for(int i=1;i<=n;++i){
		pos=-1;
		for(int j=1;j<=n;++j)
			if(!visit[j]&&(pos==-1||dis[j]<dis[pos]))pos=j;
		visit[pos]=true;
		for(int j=1;j<=n;++j)
			if(!visit[j])dis[j]=min(dis[j],dis[pos]+map[pos][j]);
	}
	printf("%d\n",dis[1]);
}
int main(){
	int t,a,b,c;
	while(scanf("%d%d",&t,&n)!=EOF){
		memset(map,inf,sizeof(map));
		while(t--){
			scanf("%d%d%d",&a,&b,&c);
			map[a][b]=map[b][a]=min(map[a][b],c);
		}
		dijkstra();
	}
	return 0;
}
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxN=1001,inf=0x3f3f3f3f;
vector<pair<int,int> > edge[maxN];
int dis[maxN],n;
void init(){
	for(int i=1;i<=n;++i){
		edge[i].clear();
		dis[i]=inf;
	}
}
void dijkstra(){
	dis[n]=0;
	priority_queue<pair<int,int> > q;
	q.push(make_pair(-dis[n],n));
	while(!q.empty()){
		int now=q.top().second;
		q.pop();
		for(int i=0;i<edge[now].size();++i){
			int v=edge[now][i].first;
			if(dis[v]>dis[now]+edge[now][i].second){
				dis[v]=dis[now]+edge[now][i].second;
				q.push(make_pair(-dis[v],v));
			} 
		}
	}
	printf("%d\n",dis[1]);
}
int main(){
	int t,a,b,c;
	while(scanf("%d%d",&t,&n)!=EOF){
		init();
		while(t--){
			scanf("%d%d%d",&a,&b,&c);
			edge[a].push_back(make_pair(b,c));
			edge[b].push_back(make_pair(a,c));
		}
		dijkstra();
	}
	return 0;
}

———————-

poj1062

题目大意:每个人有个宝贝,交换宝贝要一定的钱,但是可以用指定的其他宝贝抵一部分,每个人有等级,等级差超过了一定的范围是会拒绝交易的,也不能间接交易,即和a交易完和b交易,a和b的等级差也不能超过范围,第一个人有个妹子,问取的妹子最少要多少钱

解法:题目并没有起点,那么我们的dis数组就可以改一改,原本的意义是从源点到终点的最短路径,我们改成其他点在不用宝贝抵钱的情况下需要的钱,至于等级差,因为都要访问1,所以以1的等级为标准,遍历1的等级的左邻域,即[level(1)-m,m],对于任意的u属于那个领域,每次最短路径时只访问[u,u+m]的,最后输出最短的

#include<iostream>
#include<cstring>
#define min(a,b) (a<b?a:b)
const int maxN = 101, inf = 0x3f3f3f3f;
bool visit[maxN];
int map[maxN][maxN], dis[maxN], m, n, p[maxN], l[maxN];
int dijkstra(int level) {
	for (int i = 1; i <= n; ++i)dis[i] = p[i];
	memset(visit, false, sizeof(visit));
	for (int i = 1; i <= n; ++i)
		if (l[i] - level > m || l[i] < level)visit[i] = true;
	int pos;
	for (int i = 1; i <= n; ++i) {
		pos = -1;
		for (int j = 1; j <= n; ++j)
			if (!visit[j] && (pos == -1 || dis[j] < dis[pos]))pos = j;
		if (pos == 1)return dis[1];
		if (pos == -1)break;
		visit[pos] = 1;
		for (int j = 1; j <= n; ++j)
			if (!visit[j])dis[j] = min(dis[j], dis[pos] + map[pos][j]);
	}
	return dis[1];
}
int main() {
	int x, t, v, ans;
	while (scanf("%d%d", &m, &n) != EOF) {
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j)map[i][j] = inf;
		for (int i = 1; i <= n; ++i) {
			scanf("%d%d%d", &p[i], &l[i], &x);
			while (x--) {
				scanf("%d%d", &t, &v);
				map[t][i] = min(map[t][i], v);
			}
		}
		ans = inf;
		for (int i = l[1] - m > 0 ? l[1] - m : 0; i <= l[1]; ++i)
			ans = min(ans, dijkstra(i));
		printf("%d\n", ans);
	}
	return 0;
}

———————————–

poj1724

题目大意:求从1到n的最短距离且花费不超过k

解法:dis[maxN]改成dis[maxN][10005]因为多了一个条件,就要多存一个维度,dis[i][j]代表源点到i要花j元,剩下的就和dijkstra差不多了,只要钱超过了就不要这种状态,据说邻接矩阵会超时,用邻接表的

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxN = 101, inf = 0x3f3f3f3f;
class point {
public:
	int v, l, p;
	point(int v, int l, int p) :v(v), l(l), p(p) {}
	bool operator <(const point &t)const {
		return l>t.l;
	}
};
vector<point> edge[maxN];
int n, k, dis[maxN][10001];
void init() {
	for (int i = 1; i <= n; ++i) {
		edge[i].clear();
		for (int j = 0; j <= k; ++j)dis[i][j] = inf;
	}
}
void dijkstra() {
	dis[1][0] = 0;
	priority_queue<point> q;
	q.push(point(1, 0, 0));
	while (!q.empty()) {
		point t = q.top();
		if (t.v == n) {
			printf("%d\n", t.l);
			return;
		}
		q.pop();
		int u = t.v, l = t.l, p = t.p, price;
		for (int i = 0; i<edge[u].size(); ++i) {
			price = p + edge[u][i].p;
			int v = edge[u][i].v;
			if (price>k)continue;
			if (dis[v][price] > dis[u][p] + edge[u][i].l) {
				dis[v][price] = dis[u][p] + edge[u][i].l;
				q.push(point(v, dis[v][price], price));
			}
		}
	}
	printf("-1\n");
}
int main() {
	int r, s, d, l, t;
	while (scanf("%d%d%d", &k, &n, &r) != EOF) {
		init();
		while (r--) {
			scanf("%d%d%d%d", &s, &d, &l, &t);
			edge[s].push_back(point(d, l, t));
		}
		dijkstra();
	}
	return 0;
}

————————

poj1797

题目大意:每条边都有载重能力,求1到n的最大的载重

解法:dijkstra的变形,dis的意义改成源点到终点的最大的载重,并初始化为0,然后改一下松弛条件,从源点到i的最大载重和min(源点到now的载重,now到i的载重)取大的

#include<iostream>
#include<vector>
#include<queue>
#define min(a,b) (a<b?a:b)
using namespace std;
const int maxN = 1001;
vector<pair<int, int> > edge[maxN];
int dis[maxN], n;
void init() {
	for (int i = 1; i <= n; ++i) {
		edge[i].clear();
		dis[i] = 0;
	}
}
void dijkstra() {
	priority_queue<pair<int, int> > q;
	q.push(make_pair(1000000, 1));
	dis[1] = 1000000;
	while (!q.empty()) {
		int now = q.top().second;
		q.pop();
		for (int i = 0; i<edge[now].size(); ++i) {
			int v = edge[now][i].first;
			if (dis[v]<min(dis[now], edge[now][i].second)) {
				dis[v] = min(dis[now], edge[now][i].second);
				q.push(make_pair(dis[v], v));
			}
		}
	}
	printf("%d\n\n", dis[n]);
}
int main() {
	int t, m, a, b, c;
	scanf("%d", &t);
	for (int i = 1; i <= t; ++i) {
		scanf("%d%d", &n, &m);
		init();
		while (m--) {
			scanf("%d%d%d", &a, &b, &c);
			edge[a].push_back(make_pair(b, c));
			edge[b].push_back(make_pair(a, c));
		}
		printf("Scenario #%d:\n", i);
		dijkstra();
	}
	return 0;
}

——————————–

poj2253

题目大意:从1跳到2,求到达2的最小跳跃距离

解法:题目给的坐标,由坐标转化为距离,然后改变松弛条件,源点到i的最小跳跃距离和max(源点到now的最小跳跃距离,now到i的跳跃距离),取小的,然后依然dijkstra

#include<iostream>
#include<cmath>
#include<vector>
#include<queue>
#define max(a,b) (a>b?a:b)
using namespace std;
const int maxN=201,inf=0x3f3f3f3f;
vector<pair<int,double> > edge[maxN];
double dis[maxN];
int n;
double getDistance(const int &x1,const int &y1,const int &x2,const int &y2){
	return sqrt(1.0*(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void init(){
	for(int i=1;i<=n;++i){
		edge[i].clear();
		dis[i]=inf;
	}
}
void dijkstra(){
	priority_queue<pair<double,int> > q;
	dis[1]=0;
	q.push(make_pair(0,1));
	while(!q.empty()){
		int now=q.top().second;
		q.pop();
		for(int i=0;i<edge[now].size();++i){
			int v=edge[now][i].first;
			if(dis[v]>max(dis[now],edge[now][i].second)){
				dis[v]=max(dis[now],edge[now][i].second);
				q.push(make_pair(dis[v],v));
			}
		} 
	}
	printf("Frog Distance = %.3f\n\n",dis[2]);
}
int main(){
	int path[maxN][2],cnt=0;
	while(scanf("%d",&n)&&n){
		++cnt;
		init();
		for(int i=1;i<=n;++i)scanf("%d%d",&path[i][0],&path[i][1]);
		printf("Scenario #%d\n",cnt);
		for(int i=1;i<n;++i){
			for(int j=i+1;j<=n;++j){
				edge[i].push_back(make_pair(j,getDistance(path[i][0],path[i][1],path[j][0],path[j][1])));
				edge[j].push_back(make_pair(i,getDistance(path[i][0],path[i][1],path[j][0],path[j][1])));
			}
		}
		dijkstra();
	}
	return 0;
}

———————-

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/qq_39942341/article/details/79313727
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞