最短路模板

1、floyd-warshall

思路

枚举中转站
时间复杂度:\(O(n^3)\)
使用范围:多源点最短路

#include<bits/stdc++.h>
using namespace std;
int e[100][100],k,i,j,n,m,t1,t2,t3;
const int inf=99999;
int main()
{
	cin>>n>>m;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
			if(i==j)e[i][j]=0;
				else e[i][j]=inf;
    for(i=1;i<=m;i++)
    {
    	cin>>t1>>t2>>t3;
    	e[t1][t2]=t3;
	}
	
	for(k=1;k<=n;k++)
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
				if(e[i][j]>e[i][k]+e[k][j])
				e[i][j]=e[i][k]+e[k][j];
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			printf("%ld ",e[i][j]);
		}
	}
	return 0;
}

2、Dijkstra算法

思路:

bfs的思路,从s的邻边开始,选出最短边,再从下一个起点开始,选最短边,期间不停地更新dis,
,此时按照贪心的原理,每次选择离s最短的作为起点,标记后dis不必更新。
使用范围:无负权值的单源点最短路
时间复杂度:\(O(n^2)\)

#include <iostream>
#include <vector>
using namespace std;
struct edge{int to;int cost;};
vector<edge>g[50005];//相当于一个二维数组,存储edge 
int d[50005];
int vis[50005];
int n;
const int inf=2147483647;
int djs(int s)
{   
    int min1,k;
    for(int i=1;i<=n;i++) d[i]=inf;
        d[s]=0;//初始化 
        for(int i=1;i<n;i++)
        {
            min1=inf;k=s;

            for(int j=1;j<=n;j++)//寻找离源点最近的且未标记点
            if (!vis[j]&&min1>d[j])
                {min1=d[j];k=j;}
            vis[k]=1;//标记
            for(int j=0;j<g[k].size();j++)//重点j从0开始更新未标记的终点to
            {  
				int tm=g[k][j].to;
                if(!vis[tm]&& min1+g[k][j].cost<d[tm])
                    d[tm]=min1+g[k][j].cost; 
            }
        }
     return 0;

}

int main()
{   
	ios::sync_with_stdio(false);
    int m,s,x;
    cin>>n>>m>>s;//输入点边源
    edge tmp;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>tmp.to>>tmp.cost;
        g[x].push_back(tmp);//压入g[起点]
    }
    djs(s);
    for(int i=1;i<=n;i++)
        cout<<d[i]<<" ";

    return 0;
}

堆优化的djs

djs的思路是每次取出dis最小的点,以它为起点更新dis,并且把这个点标记为已知,那么就可以用堆得数据结构降低时间复杂度,取出最小点,并将与之连接的点放入堆中,直至堆空

#include<bits/stdc++.h>
#define re register
using namespace std;
struct edge
{
    int to,cost;
};
vector<edge>g[500005];//定义路径结构体
int n,m,s;
int dis[500005];
struct node//定义堆结构体
{
	//(如果看不懂)https://www.cnblogs.com/ZERO-/p/9347296.html
	int u,d;
	bool operator<(const node&rhs)const
	{
		return d>rhs.d;
	}
};
inline void djs()
{
	for(re int i=1;i<=n;i++)dis[i]=2147483647;
	dis[s]=0;
	priority_queue<node>Q;//初始化
	
	node a ={s,0};
	Q.push(a);//第一个node
	
	while(!Q.empty())
	{
		node fr=Q.top();Q.pop();
		int u=fr.u,d=fr.d;
		//取出并记录
		if(d!=dis[u])continue;//避免处理无用数据,也就是dis[u]已经更新,之前未更新数据直接出栈,比如有一组数据 2 5,但是后面又入栈一组数据2 3,则2 5是无用数据
		for(re int j=0;j<g[u].size();j++)
		{
			int tm=g[u][j].to;
			if(dis[u]+g[u][j].cost<dis[tm])
			{			 
            	dis[tm]=dis[u]+g[u][j].cost;
				Q.push((node){tm,dis[tm]});
			}
		}
	}
}
int main()
{
	cin>>n>>m>>s;
	int x;
	for (re int i=1;i<=m;i++)
	{
        edge tmp;
      	cin>>x>>tmp.to>>tmp.cost;
        g[x].push_back(tmp);   
    }
    djs();
 	for (re int i=1;i<=n;i++)
  	printf("%d ",dis[i]);    
	return 0;
}

3、动态规划

思路

更新更新

#include<bits/stdc++.h>
using namespace std;
long long dis[10100];
int u[500100],v[500100],w[500100],n,m,s,check;//我们定义一个check,优化用 
const int inf=2147483647;
int main()
{
    cin>>n>>m>>s;//输入 
    for(int i=1;i<=m;i++)
        cin>>u[i]>>v[i]>>w[i];//读入边 
    for(int i=1;i<=n;i++)
        dis[i]=inf;//dis数组初始化 
    dis[s]=0;
    for(int k=1;k<=n-1;k++)
    {
        check=0;//check归零 
        for(int i=1;i<=m;i++)
        {
            if(dis[v[i]]>dis[u[i]]+w[i])
            {
                dis[v[i]]=dis[u[i]]+w[i];
                check=1;//如果dis数值改变,check赋值为1 
            }   
        }
        if(check==0)
            break;//如果没变,直接跳出循环,不要浪费时间 
    }
    for(int i=1;i<=n;i++)
        cout<<dis[i]<<" ";//输出 
    return 0;//好习惯
}

4、SPFA算法

P3385 【模板】负环

#include<bits/stdc++.h>
const long long inf=2147483647;
const int maxn=10005;
const int maxm=500005;
using namespace std;
int n,m,s,num_edge=0;
int dis[maxn],vis[maxn],head[maxm];
struct Edge
{
  int next,to,dis;
}edge[maxm]; //结构体表示静态邻接表
void addedge(int from,int to,int dis) //邻接表建图
{ //以下是数据结构书上的标准代码,不懂翻书看解释
  edge[++num_edge].next=head[from]; //链式存储下一条出边
  edge[num_edge].to=to; //当前节点编号
  edge[num_edge].dis=dis; //本条边的距离
  head[from]=num_edge; //记录下一次的出边情况
}
void spfa()
{
  queue<int> q; //spfa用队列,这里用了STL的标准队列
  for(int i=1; i<=n; i++) 
  {
    dis[i]=inf; //带权图初始化
    vis[i]=0; //记录点i是否在队列中,同dijkstra算法中的visited数组
  }
  q.push(s); dis[s]=0; vis[s]=1; //第一个顶点入队,进行标记
  while(!q.empty())
  {
    int u=q.front(); //取出队首
    q.pop(); vis[u]=0; //出队标记
    for(int i=head[u]; i; i=edge[i].next) //邻接表遍历,不多解释了(也可用vector代替)
    {
      int v=edge[i].to; 
      if(dis[v]>dis[u]+edge[i].dis) //如果有最短路就更改
      {
        dis[v]=dis[u]+edge[i].dis;
        if(vis[v]==0) //未入队则入队
        {
          vis[v]=1; //标记入队
          q.push(v);
        }
      }
    }
  }
}
int main()
{
  cin>>n>>m>>s;
  for(int i=1; i<=m; i++)
  {
    int f,g,w;
    cin>>f>>g>>w; 
    addedge(f,g,w); //建图,有向图连一次边就可以了
  }
  spfa(); //开始跑spfa
  for(int i=1; i<=n; i++)
    if(s==i) cout<<0<<" "; //如果是回到自己,直接输出0
      else cout<<dis[i]<<" "; //否则打印最短距离
  return 0;
} //结束

vector版本的spfa


struct edge
{
	int v,w;
};
vector<edge>g[10001];
int d[10001];
int times[10001];
bool vis[10001];
queue<int>q;
int n;
void spfa(int s)
{
	memset(vis,0,sizeof(vis));
	memset(d,inf,sizeof(d));
	memset(times,0,sizeof(times));
	while(!q.empty())q.pop();
	q.push(s);
	vis[s]=1;
	d[s]=0;
	times[s]=1;
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		vis[now]=0;
		for(int j=0;j<g[now].size();j++)
		{
			edge p=g[now][j];
			if(d[p.v]>d[now]+p.w)
			{
				d[p.v]=d[now]+p.w;
				times[p.v]++;
				debug(times[p.v]);
				if(times[p.v]>n)
				{
					puts("YES");
					return;
				}
				if(!vis[p.v])
				{
					q.push(p.v);
					vis[p.v]=1;;
				}
			}
		}
	}
	puts("NO");
}
main(void)
{
	int t=read(),m;
	for(re int i=1;i<=t;i++)
	{
		n=read();
		m=read();
		int u,v,w;
		for(re int j=1;j<=m;j++)
		{
			edge t;
			u=read(),v=read(),w=read();
			t.v=v;
			t.w=w;
			g[u].push_back(t);
			t.v=u;
			if(w>=0)g[v].push_back(t);
		}
		spfa(1);
		for(int j=1;j<=n;j++)
			g[j].clear();
	}
}

参考:题解

posted @ 2020-08-30 10:45  王乾宇  阅读(146)  评论(0编辑  收藏  举报