最短路算法(模板)

单源最短路径

TZOJ:8537

求一个点到其他所有点的最短距离,无负权边

Dijkstra算法(优化)

用一个小顶堆去优化,每次弹出一个未标记过的最近的点,不能处理有负权边的情况

  O(mlongm)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e4+10;
vector<pair<int,int> >a[N];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >p;	
int n,m,t;
int dis[N],bj[N];
signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m>>t;
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		a[x].push_back({y,z});
	}
	for(int i=1;i<=n;i++)dis[i]=(1LL<<31)-1;
	dis[t]=0;
	for(int i=1;i<=n;i++)p.push({dis[i],i});
	int num=0;
	while(!p.empty())
	{
		num++;
		if(num==n+1)break;
		int x,Min;
		while(1)
		{
			x=p.top().second;
			Min=p.top().first;
			p.pop();
			if(!bj[x])break;
		}
		bj[x]=1;
		for(int i=0;i<a[x].size();i++)
		{
			if(!bj[a[x][i].first]&&dis[a[x][i].first]>dis[x]+a[x][i].second)
			{
				dis[a[x][i].first]=dis[x]+a[x][i].second;
				p.push({dis[a[x][i].first],a[x][i].first});
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		cout<<dis[i]<<" ";
	}
}

判断负环

TZOJ:8538

判断当前图内有无负环

bellman算法(优化)SPFA

通过记录更新点的个数,从而达到判断负环的方法,如果有负环的情况,就会一直更新下去,记录更新点情况,假如更新当前点时,之前的更新点已经大于等于n,那么就说明有负环并且还在更新,就可以返回true。

用队列去存储更新点,如果在队列中的就标记,之后如果有最短路被更新判断是否标记,没标记过就放进队列。

O(m~nm)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e3+10;
int n,m;
int dis[N],bj[N],cnt[N];
vector<pair<int,int> >a[2000+10];
bool SPFA()
{
	for(int i=1;i<=n;i++)dis[i]=1e9,cnt[i]=0,bj[i]=0;
	int p[2000+10],t=0;
	dis[1]=0;
	bj[1]=1;
	p[++t]=1;
	while(t)
	{
		int x=p[t];
		t--;
		bj[x]=0;
		for(int i=0;i<a[x].size();i++)
		{
			if(dis[a[x][i].first]>dis[x]+a[x][i].second)
			{
				dis[a[x][i].first]=dis[x]+a[x][i].second;
				cnt[a[x][i].first]=cnt[x]+1;
				if(cnt[a[x][i].first]>=n)return true;
				if(!bj[a[x][i].first])p[++t]=a[x][i].first,bj[a[x][i].first]=1;
			}
		}
	}
	return false;
} 
bool solve()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)a[i].clear();
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		if(z>=0)
		{
			a[x].push_back({y,z});
			a[y].push_back({x,z});
		}
		else a[x].push_back({y,z});
	}
	if(SPFA())return true;
	else return false;
}
signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int _;
	cin>>_;
	while(_--)
	{
		if(solve())cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
}

全源最短路

TZOJ:8539

出现负环输出-1,否则就输出各个点到其他点的最短距离之和,有负权边。

Johnson算法(SPFA+dijkstra+优化)

用一个0点与其他点全部相连,其路径都是0,再用SPFA去得到0点到第i个点的最短距离h【i】,判断过程中如果有负环就可以退出。

然后我们将每个点的权边w【u,v】更新成w'【u,v】=w【u,v】+h【u】-h【v】。

这样新的边权w'【u,v】就一定是非负数,这时候就可以用dijkstra的优化走n遍得到所有点的最短路。

O(nm logm)

证明如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e3+10;
int n,m,bj[N],dis[N],h[N],cnt[N];
vector<pair<int,int> >a[N];
queue<int>p;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >p1;
bool SPFA()
{
	for(int i=0;i<=n;i++)h[i]=1e12;
	memset(bj,0,sizeof(bj));
	memset(cnt,0,sizeof(cnt));
	bj[0]=1;
	h[0]=0;
	p.push(0);
	while(p.size())
	{
		int x=p.front();
		p.pop();
		bj[x]=0;
		for(int i=0;i<a[x].size();i++)
		{
			if(h[a[x][i].first]>h[x]+a[x][i].second)
			{
				h[a[x][i].first]=h[x]+a[x][i].second;
				cnt[a[x][i].first]=cnt[x]+1;
				if(cnt[a[x][i].first]>n)
				{
					cout<<-1<<endl;
					return true;
				}
				if(!bj[a[x][i].first])p.push(a[x][i].first),bj[a[x][i].first]=1;
			}
		}
	}
	return false;
}
void dijiestra(int x)
{
	memset(bj,0,sizeof(bj));
	for(int i=1;i<=n;i++)dis[i]=1e12;
	dis[x]=0;
	p1.push({dis[x],x});
	while(!p1.empty())
	{
		int t;
		while(p1.size())
		{
			t=p1.top().second;
			p1.pop();
			if(!bj[t])break;
		}
		bj[t]=1;
		for(int i=0;i<a[t].size();i++)
		{
			if(!bj[a[t][i].first]&&dis[a[t][i].first]>dis[t]+a[t][i].second)
			{
				dis[a[t][i].first]=dis[t]+a[t][i].second;
				p1.push({dis[a[t][i].first],a[t][i].first});
			}
		}
	}
}
void solve()
{
	cin>>n>>m;
	for(int i=0;i<=n;i++)a[i].clear();
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		a[x].push_back({y,z});
	}
	for(int i=1;i<=n;i++)a[0].push_back({i,0});
	if(SPFA())return;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<a[i].size();j++)
		{
			a[i][j].second+=h[i]-h[a[i][j].first];
		}
	}
	for(int i=1;i<=n;i++)
	{
		dijiestra(i);
		int sum=0;
		for(int j=1;j<=n;j++)
		{
			dis[j]=dis[j]-h[i]+h[j];
			if(dis[j]>1e9)dis[j]=1e9;
			sum+=dis[j]*j;
		}
		cout<<sum<<endl;
	}
	
}
signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int _=1;
	while(_--)
	{
		solve();
	}
}

无向图的最小环问题

TZOJ:8540

求一个环的边权之和最小值,这个环内的点数要大于2。

Floyd算法(优化)

不是很懂什么原理,枚举以每个点为环的两个点,通过当前点与这两个点之间是否有相连边,以及这两个点之间的最短距离,就可以判断有无环和环的边权值之和。

O(n*n*n)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e2+10;
int n,m,a[N][N],dis[N][N];
signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i!=j)a[i][j]=1e9;
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		a[x][y]=a[y][x]=min(a[x][y],z);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)dis[i][j]=a[i][j];
	int Min=1e9;
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<k;i++)
			for(int j=i+1;j<k;j++)
				Min=min(Min,dis[i][j]+a[k][i]+a[k][j]);
		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[j][k]);
	}
	if(Min==1e9)cout<<"No solution."<<endl;
	else cout<<Min<<endl;
}

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值