24 最短路问题通解

1-1 朴素Dijkstra算法

 

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//朴素Dijkstra算法,基于贪心算法
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,m;
int g[N][N];//存的a->b的距离
int dist[N];//存的是点的距离
bool st[N];//用来判断这这点是否被用过
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);//初始化每个点的距离,0x3f3f3f3f相当于正无穷
    dist[1]=0;//起点的距离初始化为0,因为起点到起点距离为0
    for(int i=0;i<n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)//这层循环找还没确定最短路中的最小值
            if(!st[j]&&(t==-1||dist[t]>dist[j))
               t=j;//更新一下最小
        st[t]=true;//把最小值记录一下,说明已经找到了这个点的最短路
        for(int j=1;j<=n;j++)//拿t来更新一下其他点的距离
            dist[j]=min(dist[j],dist[t]+g[t][j]);//取最小
    }
    if(dist[n]==0x3f3f3f3f) return -1;//假如终点没有最短路
    return dist[n];
}
int main()
{
    cin>>n>>m;
    memset(g,0x3f,sizeof g);//初始化每个为0x3f3f3f3f
   while(m--)
   {
       int a,b,c;
      scanf("%d%d%d",&a,&b,&c);//读入a->b和a->b的距离c
      g[a][b]=min(g[a][b],c);//假如有重边,则只要最小的
   }
   printf("%d\n",dijkstra());//输出从起点到终点的最短路
    return 0;
}
/*到达胜利之前无法回头*/

 1-2 堆优化的Dijkstra算法

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//堆优化的Dijkstra算法
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+10;
int n,m;
int h[N],w[N],e[N],ne[N],idx;//领接表的实现方式,w存的是权重c
int dist[N];//存的是点的距离
bool st[N];//用来判断这这点是否被用过
void add(int a,int b,int c)//把a->b链接起来
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);//初始化每个点的距离,0x3f3f3f3f相当于正无穷
    dist[1]=0;//起点的距离初始化为0,因为起点到起点距离为0
    priority_queue<PII,vector<PII>,greater<PII>>heap;//变成小根堆,不懂的话去看前面的stl
   heap.push({0,1});//把起点放进堆中,距离为0
   while(heap.size())//当堆不为空
   {
       auto t=heap.top();//取出堆顶
       heap.pop();//弹出堆顶
       int ver=t.second,distance=t.first;//ver是这个点的位置,distance是这个点的最短路
       if(st[ver]) continue;//假如已经放进堆中,则不用重新更新最短路
       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;//假如终点没有最短路
    return dist[n];
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);//初始化每个为空结点
   while(m--)
   {
       int a,b,c;
      scanf("%d%d%d",&a,&b,&c);//读入a->b和a->b的距离c
      add(a,b,c);
   }
   printf("%d\n",dijkstra());//输出从起点到终点的最短路
    return 0;
}
/*到达胜利之前无法回头*/

1.3 双端队列广搜优化的Dijkstra算法 处理边权只有0和1或者其他两个不同的边权

拖拉机

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> pii;
const int N=1e3+10;
bool g[N][N],st[N][N];
int dist[N][N];
int dx[4]={0,1,-1,0},dy[4]={1,0,0,-1};
int bfs(int sx,int sy)//双端队列广搜优化distra算法,定义草的边权为1,空地为0
{
    deque<pii> q;
    q.push_back({sx,sy});
    memset(dist,0x3f,sizeof dist);
    dist[sx][sy]=0;
    while(q.size())
    {
        auto t=q.front();
        q.pop_front();
        if(st[t.x][t.y]) continue;
        st[t.x][t.y]=true;
        if(!t.x&&!t.y) break;
        for(int i=0;i<4;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i];
            if(x>=0&&x<N&&y>=0&&y<N)
            {
                int w=0;
                if(g[x][y]) w=1;
                if(dist[x][y]>dist[t.x][t.y]+w)
                {
                    dist[x][y]=dist[t.x][t.y]+w;
                    if(!w) q.push_front({x,y});//如果是0,放在队头
                    else q.push_back({x,y});//如果是1,放在队尾
                }
            }
        }
    }
    return dist[0][0];//输出到起点的最短路
}
int main()
{
   int n,sx,sy;
   cin>>n>>sx>>sy;//终点
   while(n--)
   {
       int x,y;
       scanf("%d%d",&x,&y);
       g[x][y]=true;
   }
   cout<<bfs(sx,sy);
    return 0;
}

 2-1 Bellman-Ford算法

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//Bellman-Ford算法
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int dist[N],backup[N];//dist存的是最短路,backup是用来备份
int n,m,k;
struct Edge//建立一个结构体来存三个数,分别是a->b和权重w
{
    int a,b,w;
}edges[N];
int bellman_ford()
{
    memset(dist,0x3f,sizeof dist);//初始化除起点外距离为正无穷
    dist[1]=0;//起点初始化为0
    for(int i=0;i<k;i++)
    {
        memcpy(backup,dist,sizeof dist);//备份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);//找最小距离,更新最短路
        }
    }
   if(dist[n]>0x3f3f3f3f/2) return 0x3f3f3f3f;//假如不存在最短路
    return dist[n];
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
   for(int i=0;i<m;i++)
   {
       int a,b,w;
       scanf("%d%d%d",&a,&b,&w);
       edges[i]={a,b,w};
   }
  int t=bellman_ford();
  if(t==0x3f3f3f3f) puts("impossible");//假如不存在最短路
  else printf("%d\n",t);
    return 0;
}
/*到达胜利之前无法回头*/

2-2 spfa算法基本通解所有最短路,正权一般也行,而且时间快 

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//spfa算法,也可以解正权的数
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m;
int h[N],w[N],e[N],ne[N],idx;//领接表的实现方式,w存的是权重c
int dist[N];//存的是点的距离
bool st[N];//用来标记这个点是否在队列中
void add(int a,int b,int c)//把a->b链接起来
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
    memset(dist,0x3f,sizeof dist);//初始化每个点的距离,0x3f3f3f3f相当于正无穷
    dist[1]=0;//起点的距离初始化为0,因为起点到起点距离为0
    queue<int> q;//定义一个队列
    q.push(1);//把起点1放进队列中
    st[1]=true;//标记1在队列中
    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()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);//初始化每个为空结点
   while(m--)
   {
       int a,b,c;
      scanf("%d%d%d",&a,&b,&c);//读入a->b和a->b的距离c
      add(a,b,c);
   }
   int t=spfa();
   if(t==0x3f3f3f3f) puts("impossible");//因为距离还是正无穷,所有没有最短路
   else  printf("%d\n",t);//输出从起点到终点的最短路
    return 0;
}
/*到达胜利之前无法回头*/

 2-2-1 spfa判断是否有负环

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//spfa算法,也可以解正权的数
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m;
int h[N],w[N],e[N],ne[N],idx;//领接表的实现方式,w存的是权重c
int dist[N],cnt[N];//dist存的是点的距离,cnt存的是边数
bool st[N];//用来标记这个点是否在队列中
void add(int a,int b,int c)//把a->b链接起来
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int 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;//j的边数加一
                if(cnt[j]>=n) return true;//假如j的边数大于n,说明有相同的点,则有负环
                if(!st[j])//假如这个点不在队列中
                {
                    q.push(j);//则放进队列中
                    st[j]=true;//标记这个点已经放进了队列中
                }
            }
        }
    }
    return false;//否则没有负环
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);//初始化每个为空结点
   while(m--)
   {
       int a,b,c;
      scanf("%d%d%d",&a,&b,&c);//读入a->b和a->b的距离c
      add(a,b,c);
   }
   if(spfa()) puts("Yes");//有负环
   else  puts("No");
    return 0;
}
/*到达胜利之前无法回头*/

3  Floyd算法处理多源最短路

/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//Floyd算法处理多个起点的问题,可以求出任意一个点到另一个任意点的距离
#include<bits/stdc++.h>
using namespace std;
const int N=210,INF=0x3f3f3f3f;
int n,m,Q;
int d[N][N];//定义一个领接矩阵存a->b的距离d
void floyd()//三层循环实现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()
{
    scanf("%d%d%d",&n,&m,&Q);
    for(int i=1;i<=n;i++)//初始化领接矩阵
        for(int j=1;j<=n;j++)
            if(i==j) d[i][j]=0;//i==j,说明点的距离为0
            else d[i][j]=INF;//其他点定义正无穷
    while(m--)
    {
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        d[a][b]=min(d[a][b],w);//距离去a->b的较小者
    }
    floyd();//调用函数
    while(Q--)//Q次询问
    {
        int a,b;
        scanf("%d%d",&a,&b);//读入要求的a->b的距离最短路
        if(d[a][b]>INF/2) puts("impossible");//假如不存在
        else printf("%d\n",d[a][b]);
    }
    return 0;
}
/*到达胜利之前无法回头*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值