最短路

1.dijkstra算法

用于计算一个节点到达其他任何结点的最短距离,要求图中不存在负权边

 

思路:







模板:

const int INF = 0x03f3f3f3f;
const int maxn = 100;
int preV[maxn];              //最短路径树中的前驱结点信息表,preV[x]=y : y是x的前驱结点
int vis[maxn];               //结点是否加入S的标记,1为已加入,2为仍在V-S中
int low[maxn];               //low[i]:从原点到节点i的最短受限路径长度,若i已加入S,则是源到i的最短路长,否则就是以S中的点为媒介的从源到i的最短路径
int G[maxn][maxn];           //邻接矩阵,无路径则数值为INF,对角线为0
int beginV;                  //原点
int numV;                    //结点个数

void Dij()              //带路径保存
{
    vis[beginV]=1;
    for(int i = 0 ; i < numV ; i++) //初始S中只有源点,各点的受限距离就是其到源点的距离
    {
        low[i] = G[beginV][i];
        preV[i]=beginV;
    }

    preV[beginV] = -1;  //源点无前驱

    int pos = beginV;

    for(int i = 1 ; i < numV ; i++)     //更新剩下的numV-1个节点
    {
        for(int j = 0 ; j < numV ; j++)
            if(!vis[j] && low[j] > low[pos] + G[pos][j])
            {
                low[j] = low[pos] + G[pos][j];
                preV[j] = pos;
            }

        int minc = INF;

        for(int j = 0 ; j < numV ; j++)     //选择最短受限路径
            if(!vis[j] && low[j] < minc)
            {
                minc = low[j];
                pos = j;
            }
        vis[pos] = 1;           //下一个被选中的顶点
    }
}

int Dij2()         //不保存路径
{
    vis[beginV]=1;

    for(int i = 0 ; i < numV ; i++)
        low[i] = G[beginV][i];

    for(int i = 1 ; i < numV ; i++)
    {
        int minc = INF;
        int pos,weight=0;                       //weight - 最短路长
        for(int j = 0 ; j < numV ; j++)
            if(!vis[j] && low[j] < minc)        //由于low[beginV]肯定是最小的0,所以可以不用预处理了
            {
                minc = low[j];
                pos = j;
            }

        vis[j]=1;
        weight += minc;

        for(int j = 0 ; j < numV ; j++)
            if(!vis[j] && low[j] > low[pos] + G[pos][j])
                low[j] = low[pos] + G[pos][j];

    }

    return weight;
}

2. Floyd算法

所有点之间的最短路

 

模板:

inf = 0x3fffffff
void floyd()
{
       for(intk = 0 ; k < n ; k++)
              for(inti = 0 ; i < n ; i++)
                     if(dis[i][k]< inf)
                            for(intj = 0; j < n ; j++)
                                   if(i!=j&& dis[i][j] > dis[i][k]+dis[k][j])
                                          dis[i][j]= dis[i][k]+dis[k][j];
}

3.SPFA算法

思路:

建立一个队列,初始时队列里只有起始点,在建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空

 

判断有无负环:

如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

 

模板:

SPFA 算法(邻接矩阵)

void spfa(int n ; int src)          //顶点个数,起始点
{
    que.push(src);
    low[src]=0;
    vis[src]=1;
    while(!que.empty())
    {
        int cur = que.front();
        que.pop();
        vis[cur]=0;
        for(int i = 1 ; i <= n ; i++)
            if(low[cur]+G[cur][i] <low[i])
            {
                low[i]=low[cur]+G[cur][i];
                if(!vis[i])
                {
                    que.push(i);
                    cnt[i]++;
                    vis[i]=1;
                }

                if(cnt[i] > n)
                    //出现负环,终止
            }
    }
}


SPFA算法(邻接表)

#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 1<<30;
const int MAXN = 1000005;

int n,m;

struct edge     //边结构体,now是有向边的开始结点,next_edge是下一条边的始结点的编号,weight是权重
{
   int now;
   int next_edge;
   int weight;
}e[MAXN];        //边数组

int head[MAXN];  //head[x] : x顶点所在边的编号
int cnt=0;

int low[MAXN];   //储存源点到每个顶点的最短路
int vis[MAXN];   //标记是否在队列里

void add(int x,int y,int v)      //加边
{
    e[cnt].now = y;
    e[cnt].next_edge = head[x];      //这句和第四句不能交换位置
    e[cnt].weight = v;
    head[x] = cnt++;
}

void spfa(int st)
{
    low[st]=0;
    queue<int> que;
    que.push(st);
    vis[st]=1;

    while(!que.empty())
    {
        int cur = que.front();
        que.pop();
        vis[cur]=0;

        for(int i = head[cur] ; i != -1 ; i = e[i].next_edge)
            if(low[e[i].now] > low[cur] + e[i].weight)
            {
                low[e[i].now] = low[cur] + e[i].weight;

                if(!vis[e[i].now])
                {
                    que.push(e[i].now);
                    vis[e[i].now]=1;
                }
            }
    }

}

int main()
{
    int t;
    cin >> t;

    while(t--)
    {
        cin >> n >> m;
        memset(vis,0,sizeof(vis));
        for(int i = 0 ; i <= n ; i++)
            low[i] = INF;
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i = 0 ; i < m ; i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
        }

        spfa(1);

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值