图论最短路复习

朴素Dijkstra

https://www.acwing.com/problem/content/description/851/

O(N^2)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>

using namespace std;

const int INF = 0x3f3f3f3f;

const int N = 510;

int g[N][N];//边
int dist[N];//最短距离
bool st[N];

int n,m;


int dijkstra()
{
	memset(dist,INF,sizeof dist);
	dist[1]=0;
	for(int i=0;i<n;i++)
	{
		int t=-1;
		for(int j=1;j<=n;j++)//寻找距离起点最短的t点
			if(!st[j] && (t==-1 || dist[t]>dist[j]))
				t=j;
		if(t==n)break;//优化,t直接是终点,则直接找到了最短路
		st[t]=true;
		for(int j=1;j<=n;j++)
			dist[j]=min(dist[j],dist[t]+g[t][j]);//用t更新其它点的距离
	}
	if(dist[n]==INF)return -1;//1与n不连通
	else return dist[n];
}

int main()
{
	cin >> n >> m;
	memset(g,INF,sizeof g);
	for(int i=0;i<m;i++)
	{
		int x,y,z;
		cin >> x >> y >> z;
		g[x][y]=min(g[x][y],z);
	}
	int t=dijkstra();
	cout << t << endl;
}

对于稀疏图(n>1e5),采用堆优化版Dijkstra( O(mlogn) )
这里使用优先队列实现
https://www.acwing.com/problem/content/description/852/

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>

using namespace std;

typedef pair<int,int>PII;

const int N = 1e6+10;

int h[N],e[N],ne[N],w[N],idx;//邻接表存储

int dist[N];
bool st[N];

int n,m;

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


int dijkstra()
{
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	priority_queue<PII,vector<PII>,greater<PII>> heap;
	heap.push({0,1});//起点
	while(heap.size())
	{
		auto t = heap.top();
		heap.pop();
		int ver=t.second,distance=t.first;
		if(st[ver])continue;//ver此前已经出现过
		st[ver]=true;
		for(int i=h[ver];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(dist[j]>distance+w[i])
			{
				dist[j]=distance+w[i];
				heap.push({dist[j],j});
			}
		}
	}
	if(dist[n]==0x3f3f3f3f)return -1;//1与n不连通
	else return dist[n];
}

int main()
{
	cin >> n >> m;
	memset(h,-1,sizeof h);
	for(int i=0;i<m;i++)
	{
		int x,y,z;
		cin >> x >> y >> z;
		add(x,y,z);
	}
	int t=dijkstra();
	cout << t << endl;
}

 

Bellman-Ford算法求最短路(O(NM))
https://www.acwing.com/problem/content/855/

Bellman-Ford对于边很宽容,用一个结构体存储即可,它只需要能够被访问即可

//Bellman-Ford算法求含负权边最短路
//若图中存在负权回路则最短路不一定存在(负环在最短路径上,则最短路会变成负无穷,不若在最短路径上,则存在最短路)
//O(nm)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 510,M=10010;

int dist[N],backup[N];

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

int n,m,k;

int bellman_ford()
{
	memset(dist,0x3f,sizeof dist);//初始化
	dist[1]=0;//初始化
	for(int i=0;i<k;i++)
	{
		memcpy(backup,dist,sizeof dist);
		for(int j=0;j<m;j++)
		{
			int a=edges[j].a,b=edges[j].b,w=edges[j].w;
			dist[b]=min(dist[b],backup[a]+w);
		}
	}
	return dist[n];
}


int main()
{
	cin >> n >> m >> k;
	for(int i=0;i<m;i++)
	{
		int a,b,w;
		cin >> a >> b >> w;
		edges[i]={a,b,w};
	}
	int t=bellman_ford();
	if(t>0x3f3f3f3f/2)cout << "impossible" << endl;
	else cout << t << endl;
}

spfa求含负权边最短路(O(m),最坏O(nm))

https://www.acwing.com/problem/content/853/

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>

using namespace std;

typedef pair<int,int>PII;

const int N = 1e6+10;

int h[N],e[N],ne[N],w[N],idx;//邻接表存储

int dist[N];//最短距离
bool st[N];

int n,m;

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


int spfa()
{
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	queue<int>q;
	q.push(1);
	st[1]=true;//st防止存储重复点
	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(dist[j]>dist[t]+w[i])
			{
				dist[j]=dist[t]+w[i];
				if(!st[j])
				{
					q.push(j);
					st[j]=true;
				}
			}
		}
	}
	return dist[n];
}

int main()
{
	cin >> n >> m;
	memset(h,-1,sizeof h);
	for(int i=0;i<m;i++)
	{
		int x,y,z;
		cin >> x >> y >> z;
		add(x,y,z);
	}
	int t=spfa();
	if(t==0x3f3f3f3f)cout << "impossible" <<endl;
	else cout << t << endl;
}

spfa判断负环

https://www.acwing.com/problem/content/description/854/

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>

using namespace std;

typedef pair<int,int>PII;

const int N = 1e6+10;

int h[N],e[N],ne[N],w[N],idx;//邻接表存储

int cnt[N];//维护一个表示从起点走到i的经过的边数的数组,一旦cnt[x]>=n,则表示在1~x中存在负环
//cnt[x]>=n,则表示1~x个点,却经历了n条边,那么根据抽屉原理一定存在重复点
//重复点就说明存在环,由于算法是更新更加短的路,存在环那么总路径一定会变短,变短就说明是负环
//若是正环,那么dist必定增加,不会选择走环,边数最多为n-1
int dist[N];//最短距离
bool st[N];

int n,m;

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


bool spfa()
{
//	因为只需要判断负环,所以无需初始化了
	queue<int>q;
//	队列中不只是需要第一个点了,而是需要所有点,因为单从第一个点不一定能走到负环
	for(int i=1;i<=n;i++)
	{
		st[i]=true;
		q.push(i);
	}
	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(dist[j]>dist[t]+w[i])
			{
				dist[j]=dist[t]+w[i];
				cnt[j]=cnt[t]+1;
				if(cnt[j]>=n)return true;
				if(!st[j])
				{
					q.push(j);
					st[j]=true;
				}
			}
		}
	}
	return false;
}

int main()
{
	cin >> n >> m;
	memset(h,-1,sizeof h);
	for(int i=0;i<m;i++)
	{
		int x,y,z;
		cin >> x >> y >> z;
		add(x,y,z);
	}
	if(spfa())cout <<"Yes" << endl;
	else cout << "No" << endl;
}

floyd多源汇最短路(O(N^3))

https://www.acwing.com/problem/content/856/

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>



using namespace std;

const int INF = 0x3f3f3f3f;


const int N = 210;

int n,m,k,Q;
int d[N][N];//算法执行前是领接矩阵,执行后是点之间的最短距离

void floyd()
{
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}


int main()
{
	cin >> n >> m >> Q;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i==j)d[i][j]=0;//删除自环
			else d[i][j]=INF;
	while(m--)
	{
		int a,b,w;
		cin >> a >> b >> w;
		d[a][b]=min(d[a][b],w);//多条边只保留最小边
	}
	floyd();
	while(Q--)
	{
		int a,b;
		cin >> a>> b;
		//>INF/2的原因是若存在负权边,尽管两点之间不连通,却仍然会更新距离,会稍微比INF小一些,导致不连通却不等于INF
		if(d[a][b] > INF/2)cout << "impossible" << endl;
		else cout << d[a][b] << endl;
	}
}

 

posted @   风乐  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示