最短路算法

被这几个板子折磨,打一打最近学的最短路模板

1.Floyd朴素算法

优点:编程复杂度低,并且状态转移方程可以多变
缺点:时空复杂度高,不易优化

最短路
#include <bits/stdc++.h>
using namespace std;
#define map mymap
const int p=100+1;
int n,m,s,t;
int ans;
int map[p][p];
int main(){
    cin>>n>>m>>s>>t;
    memset(map,0x3f,sizeof(map));
    int x,y;
    for(int i=1;i<=m;i++){
        cin>>x>>y;
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
            }
        }
    }
    cout<<map[s][t];
    return 0;
}
最小环
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

	int w[105][105];
	int s[105][105];
	int n,m;
	int x,y,l;
	int ans=0x3f3f3f3f;

int main()
{
	scanf("%d%d",&n,&m);
	memset(w,0x3f3f3f3f,sizeof(w));
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&l);
		w[x][y]=min(w[x][y],l);
		w[y][x]=min(w[y][x],l);
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			s[i][j]=w[i][j];
		}
	}
	
	for (int k=1;k<=n;k++)
	{
		for (int i=1;i<k;i++)//判断回路 
		{
			for (int j=i+1;j<k;j++)
				ans=min(ans,w[i][j]+s[i][k]+s[k][j]);
        //此时 w[i][j]最短路一定不经过k点,因为更新还没有更新到k,0到k-1更新完毕,所以这个式子才是判断回路 
			
		}
		for (int i=1;i<=n;i++)
		{
			for (int j=1;j<=n;j++)
			{
				w[i][j]=min(w[i][j],w[i][k]+w[k][j]);
			}
		}
	}
		
	printf("%d\n",ans);
	
	return 0;
	}

2.Dijsktra迪杰斯特拉算法

优点:时间复杂度较小,为O(n^2)级(应用数据规模从500到10000),可以被优化,复杂度稳定
Upd:其实是mlogn反正远古博客直接就混乱邪恶吧
缺点:无法处理负边权问题,单源
如果有特殊性质还是可以跑负权的:把连通块看成广义点以后整张图是DAG
1.无优化

普通的Dijsktra算法
#include <bits/stdc++.h>
using namespace std;
int a[101][3];
double c[101];
bool b[101];
double f[101][101];
int n,m,s,t;
void in(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a[i][1],&a[i][2]);
    }
    memset(f,127,sizeof(f));
    scanf("%d",&m);
    int x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        f[x][y]=f[y][x]=sqrt(pow(double(a[x][1]-a[y][1]),2)+pow(double(a[x][2]-a[y][2]),2));
    }
    scanf("%d%d",&s,&t);
}
void dij(){
    memset(b,0,sizeof(b));
    b[s]=1;
    c[s]=0;
    for(int i=1;i<=n;i++){
        c[i]=f[s][i];
    }
    double minl;
    for(int i=1;i<n;i++){
        minl=1e30;
        int k=0;
        for(int j=1;j<=n;j++){
            if((!b[j])&&(c[j]<minl)){
                minl=c[j];
                k=j;
            }
        }
		if(k==0){
	            break;
        }
        b[k]=1; 
        for(int j=1;j<=n;j++){
            if(c[k]+f[k][j]<c[j]){
                c[j]=c[k]+f[k][j];
            }
        }
   }
}
void out(){
    printf("%.2lf",c[t]);
}
int main(){
    in();
    dij();
    out();
    return 0;
}
2.priority_queue优化
优先队列优化
#include <bits/stdc++.h>
using namespace std;
const int o=1e5;
int n,m,s,t,cnt,x,y,z;
int dis[o],head[o];
bool vis[o];
struct path{
	int s;
	int t;
	int v;
	int n;
}a[o];
struct node{
	int id;
	int exp; 
}p;
struct cmp{
	bool operator()(node a,node b){
		return a.exp>b.exp;
	} 
};
priority_queue<node,vector<node>,cmp>q;
void add(int st,int t,int v){
	cnt++;
	a[cnt].s=st;
	a[cnt].t=t;
	a[cnt].v=v;
	a[cnt].n=head[st];
	head[st]=cnt;
}
void in(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
}
void dij(int s){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	dis[s]=0;
	p.id=s;
	p.exp=0;
	q.push(p);
	while(!q.empty()){
		p=q.top();
		q.pop();
		int to=p.id;
		if(vis[to]){
			continue;
		}
		vis[to]=1;
		for(int i=head[to];i>0;i=a[i].n){
			int j=a[i].t;
			if(dis[j]>dis[to]+a[i].v){
				dis[j]=dis[to]+a[i].v;
				if(!vis[j]){
					node r;
					r.id=j;
					r.exp=dis[j];
					q.push(r);
				}
			}
		}
	}
}
void out(){
	cout<<dis[t];
}
int main(){
	in();
	dij(s);
	out();
	return 0;
}

3.spfa算法

优点:可解负边权问题,时间复杂度为O(kn),k常数较小,因此快,可优化
缺点:可被特殊构造的数据卡掉退化为O(mn)
upd:当时写的东西看不明白,但是尽量别写别的直接卡成指数
然后卡SPFA学了网格图

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define map mymap
queue<int>q;
int n,m,map[2021][2022],dis[2005];
bool vis[2005];
void spfa(){
	q.push(1);
	vis[1]=true;
	dis[1]=0;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		vis[x]=false;
		for(int i=1;i<=n;i++){
			if(map[x][i]>0&&(min(dis[x],map[x][i])>dis[i]||!dis[x])){
				if(!dis[x]){
					dis[i]=map[x][i];
				}
				else{
					dis[i]=min(dis[x],map[x][i]);
				}
				if(!vis[i]){
					vis[i]=true;
					q.push(i);
				}
			}
		}
	}
} 
int main(){
	scanf("%d",&n);
	memset(map,0,sizeof(map));
	for(int i=2;i<=n;i++){
		dis[i]=0;
	}
	int f,t,p;
	while(scanf("%d%d%d",&f,&t,&p)==3){
		if(!f&&!t&&!p){
			break;
		}
		else{
			map[f][t]=p;
		}
	}
	spfa();
	for(int i=2;i<=n;i++){
		printf("%d\n",dis[i]);
	}
	return 0;
}

再贴一下二维SPFA和SPFA判断有无负环然后跑路
upd:二维实际上就是做n次SPFA,判断负环入队次数考虑k的大小
随机图上k大于3直接认定有负环问题并不是非常大但是还是错的

二维SPFA
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std ;

const int INF=0xfffffff ;
struct node{
    int s,t,v,nxt ;
}e[1000005] ;

int n,m,k,cnt,head[100005],vis[100005][55],dis[100005][55] ;

void add(int s,int t,int v)
{
    e[cnt].s=s ;
    e[cnt].t=t ;
    e[cnt].v=v ;
    e[cnt].nxt=head[s] ;
    head[s]=cnt++ ;
}

void spfa(int s)
{
    for(int i=0 ;i<=n ;i++)
        for(int j=0 ;j<55 ;j++)
            dis[i][j]=INF ;
    dis[s][0]=0 ;
    memset(vis,0,sizeof(vis)) ;
    vis[s][0]=1 ;
    queue <pair<int,int> > q ;
    q.push(make_pair(s,0)) ;
    while(!q.empty())
    {
        pair<int,int> u=q.front() ;
        q.pop() ;
        vis[u.first][u.second]=0 ;
        int step=u.second+1 ;
        if(step>k)step=k ;
        for(int i=head[u.first] ;i!=-1 ;i=e[i].nxt)
        {
            int tt=e[i].t ;
            if(dis[tt][step]>dis[u.first][u.second]+e[i].v)
            {
                dis[tt][step]=dis[u.first][u.second]+e[i].v ;
                if(!vis[tt][step])
                {
                    vis[tt][step]=1 ;
                    q.push(make_pair(tt,step)) ;
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        cnt=0 ;
        memset(head,-1,sizeof(head)) ;
        while(m--)
        {
            int a,b,c ;
            scanf("%d%d%d",&a,&b,&c) ;
            add(a,b,c) ;
            add(b,a,c) ;
        }
        int s,t ;
        scanf("%d%d%d",&s,&t,&k) ;
        k=k/10+(k%10!=0) ;
        spfa(s) ;
        if(dis[t][k]==INF)puts("-1") ;
        else printf("%d\n",dis[t][k]) ; 
    }
    return 0 ;
}
SPFA判断负环
#include<iostream> 
#include<queue> 
#include<cstring> 
#include<cstdio>
using namespace std; 
int const maxNum=1001; 
int const Infinity=99999999; 
 
int map[maxNum][maxNum],dis[maxNum];//dis用来存放点的最佳路径 
int nodeNum,time[maxNum];//time用来记录结点入队的次数 
int vst[maxNum],visited[maxNum];//标志是否入队,标志点是否扫描过 
 
bool SPFA(int start)//经典的SPFA算法 
{ 
    int i,p; 
    queue<int> que; 
    memset(vst,0,sizeof(vst)); 
    memset(time,0,sizeof(time)); 
 
    for(i=1;i<=nodeNum;i++) 
        dis[i]=Infinity; 
     
    dis[start]=0; 
    vst[start]=1; 
    que.push(start); 
    time[start]++;//起点先标志入队一次 
    while(!que.empty()) 
    { 
        p=que.front(); 
        que.pop(); 
        vst[p]=0;         
        for(i=1;i<=nodeNum;i++) 
        { 
            visited[i]=true;//这里用来标志已经被扫描过的点。注意visited跟vst数组的区别 
            if(dis[p]+map[p][i]<dis[i]) 
            { 
                dis[i]=dis[p]+map[p][i]; 
                if(!vst[i]) 
                { 
                    que.push(i); 
                    time[i]++; 
                    if(time[i]>nodeNum)//当同一结点入队次数超过点的总数-1,即大于等于nodeNum时,存在负环,此题的关键
                        return true; 
                    vst[i]=1; 
                } 
            } 
        } 
    } 
    return false; 
} 
 
int main(void) 
{ 
    int cas,n,num,i,s,e,w,j; 
    scanf("%d",&cas);//田地的个数 
    while(cas--) 
    { 
        scanf("%d%d%d",&nodeNum,&n,&num);//结点数,边数,虫洞数 
        for(i=1;i<=nodeNum;i++) 
            for(j=1;j<=nodeNum;j++) 
                map[i][j]=Infinity; 
        for(i=1;i<=n;i++) 
        { 
            scanf("%d%d%d",&s,&e,&w);//边的起点,边的终点,走这条边所花的时间 
            if(map[s][e]>=w) 
            { 
                map[s][e]=w;//注意重边这种情况,取最小的那个 
            } 
            if(map[e][s]>=w)//双向边,应该每次都比较一下,(但是这道题目不比较也过) 
            { 
                map[e][s]=w; 
            } 
        } 
        for(i=1;i<=num;i++) 
        { 
            scanf("%d%d%d",&s,&e,&w); 
            if(map[s][e]>-w) 
                map[s][e]=-w;//这道题目这里似乎说明得不是很严谨,如果一条边原来是Infinity,而现在有一个负的边,那不就替代了么 
                             //题目似乎被理想化了,这种情况不考虑在其中,不知道说得对不对,望高手指教 
        } 
        memset(visited,0,sizeof(visited)); 
        for(i=1;i<=nodeNum;i++)//考虑到图可能不是完全连通图,即存在离散的子图 
        { 
            if(visited[i])    continue;//增加这一步以减少不必要的计算 
            if(SPFA(i)) 
            { 
                cout<<"YES"<<endl; 
                break; 
            } 
        }                                                                                                                                           if(i==nodeNum+1) 
            cout<<"NO"<<endl; 
    } 
    return 0; 
} 
posted @   2K22  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示