图论 - 最短路,dijkstra朴素版及优化版, bellman_ford, spfa, floyd模板

请添加图片描述
稠密图用邻接矩阵存储,稀疏图用邻接表存储

邻接表存储图的方式
int h[N];// 存储点,h[a]表示点a指向的一条边
int e[N];// 存储边,e[idx]表示边idx指向的点
int ne[N];// 存储边,e[idx]表示和边idx同起点边
int w[N];// 存储权重,w[idx]表示边idx的权重
int idx;// 边的编号

void add(int a, int b, int c){
    e[idx] = b;// idx表示又新增了一条边(结合idx++来看),e[idx]表示这条边指向点b
    ne[idx] = h[a];// 和新增边idx同起点的边,也就是起点同为a的边,h[a]赋值给ne[idx]
    w[idx] = c;// 新增边的权重是c
    h[a] = idx++;// 链表的头节点指向新增边idx
}
朴素版Dijkstra

HDU - 2544

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<string>
#include<set>

using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=1e3+5;

int n,m;
int g[N][N];// 邻接矩阵存储图
int dis[N];// 表示各个点到源点的距离,dijkstra就是将此数组更新n-1次
bool st[N];

int dijkstra(){
	
	dis[1]=0;
	for(int i=0;i<n-1;i++){
		int t=-1;
		for(int j=1;j<=n;j++)
			if(!st[j]&&(t==-1||dis[t]>dis[j]))
				t=j;
		for(int j=1;j<=n;j++)
			dis[j]=min(dis[j],dis[t]+g[t][j]);
		// dis[j]:源点直接到点j的距离
		// dis[t]+g[t][j]:源点先到点t,再从t到点j的距离
		st[t]=true;
	}
	return dis[n];
}

int main(void){
	while(~scanf("%d%d",&n,&m)&&n!=0&&m!=0){
		memset(dis,INF,sizeof dis);
		memset(g,INF,sizeof g);
		memset(st,false,sizeof st);
		for(int i=0;i<m;i++){
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			g[a][b]=g[b][a]=min(g[a][b],min(g[a][b],c));
		}
		printf("%d\n",dijkstra());
	}

	return 0;
}

/*
 * When ability does not derserve ambition, just keep moving
 */
堆优化版Dijkstra

AcWing - 850

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<string>
#include<set>

using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
typedef pair<int,int> PII;

int n, m;
int h[N],e[N],ne[N],w[N],idx;
int dis[N];
bool st[N];

void add(int a,int b,int c){
	e[idx]=b;
	ne[idx]=h[a];
	w[idx]=c;
	h[a]=idx++;
}

int dijkstra(){
	memset(dis,INF,sizeof dis);
	priority_queue<PII, vector<PII>, greater<PII>> heap;
	
	heap.push({0,1});
	
	while(heap.size()){
		auto t=heap.top();
		heap.pop();
		
		int v=t.second,dist=t.first;
		
		if(st[v])
			continue;
		st[v]=true;
		
		for(int i=h[v];i!=-1;i=ne[i]){
			int j=e[i];
			if(dis[j]>w[i]+dist){
				dis[j]=w[i]+dist;
				heap.push({dis[j],j});
			}
		}
	}
	if(dis[n]==INF)
		return -1;
	return dis[n];
	
}

int main(void){
	scanf("%d%d",&n,&m);
	memset(h,-1,sizeof h);
	while(m--){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
	}	
	cout<<dijkstra()<<endl;

	return 0;
}

/*
 * When ability does not derserve ambition, just keep moving
 */
Bellman_Ford

AcWing - 855

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<string>
#include<set>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 500 + 5;
const int M = 1e4 + 10;

int n, m, k;
int dis[N], backup[N];

struct Edge {
	int a, b, w;
} edge[M];

int bellman_ford() {
	memset(dis, INF, sizeof dis);
	dis[1] = 0;

	for (int i = 0; i < k; i++) {
		memcpy(backup, dis, sizeof dis);
        // memcpy操作可以根据样例
        // 4 4 1
        // 1 2 1
        // 2 3 1
        // 3 4 1
        // 1 3 -2
        // 得出原因,主要是有边数限制
        // 如果不使用上一次的更新结果,那么可能第一次更新便可能根据遍历顺序,直接走到终点
        // 但是这样就超出边数限制了,所以不可取
		for (int j = 0; j < m; j++) {
			int a = edge[j].a;
			int b = edge[j].b;
			int w = edge[j].w;
			dis[b] = min(dis[b], backup[a] + w);
		}
	}
	return dis[n];
}

int main(void) {
	scanf("%d%d%d", &n, &m, &k);

	for (int i = 0; i < m; i++) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		edge[i] = {a, b, c};
	}

	int t = bellman_ford();

	if (t > INF / 2)
		puts("impossible");
	else
		printf("%d\n", t);

	return 0;
}

/*
 * When ability does not derserve ambition, just keep moving
 */
SPFA

AcWing - 851

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<string>
#include<set>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 5;

int n, m;
int dis[N];
bool st[N];
int h[N], e[N], ne[N], w[N], idx;

void add(int a, int b, int c) {
	e[idx] = b;
	ne[idx] = h[a];
	w[idx] = c;
	h[a] = idx++;
}

int spfa() {
	memset(dis, INF, sizeof dis);
	dis[1] = 0;

	queue<int> q;
	q.push(1);

	st[1] = true;

	while (q.size()) {
		int t = q.front();
		q.pop();

		st[t] = false;

		for (int i = h[t]; i != -1; i = ne[i]) {
			int j = e[i];
			if (dis[j] > dis[t] + w[i]) {
				dis[j] = dis[t] + w[i];
				if (!st[j]) {
					q.push(j);
					st[j] = true;
				}
			}
		}
	}
	return dis[n];
}

int main(void) {
	memset(h, -1, sizeof h);
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);

		add(x, y, z);
	}
	int ans = spfa();
	if (ans > INF / 2)
		puts("impossible");
	else
		printf("%d\n", ans);
	return 0;
}

/*
 * When ability does not derserve ambition, just keep moving
 */
Floyd

HDU - 2544

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<string>
#include<set>

using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=1e3+5;

int n,m;
int dis[N][N];
bool st[N];

int floyd(){
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
	return dis[1][n];
}
void init() {
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (i == j)
				dis[i][j] = 0;
			else
				dis[i][j] = INF;
}
int main(void){
	while(~scanf("%d%d",&n,&m)&&n!=0&&m!=0){
		init();
		memset(dis,INF,sizeof dis);

		for(int i=0;i<m;i++){
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			dis[a][b]=dis[b][a]=min(dis[a][b],min(dis[a][b],c));
		}
		printf("%d\n",floyd());
	}

	return 0;
}

/*
 * When ability does not derserve ambition, just keep moving
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值