Dijkstra 算法的几种变形

Dijkstra用来就最短路径,在保证最短路径的条件下,可新增一些其他标尺。

1.每个结点拥有点权,在最短路劲的条件下求点权和最大的路劲。

2.每条边新增边权cost,在最短路径的条件下求cost和最小的路径。

3.求最短路径的条数。


问题1,3可参考我的另一篇文章:

https://blog.csdn.net/qq_39304201/article/details/80176476

关键是建立num数组与maxWeight数组。num[i]表示从起点到结点i的最短路径条数,maxWeight[i]表示起点到结点i最大权值和。在更新最短路径时同时更新这两个数组即可。

问题2与问题1差不多,建立cost[]数组,求起点到每个结点i的最小花费cost[i];


问题1代码:

#include <iostream>
const int MAX = 999999;
using namespace std;


void findPath(int dest,int *p)
{
    if(p[dest] == -1)
    {
        cout<<dest;
        return ;

    }
    findPath(p[dest],p);
    cout<<"-"<<dest;
}
int main()
{
    int n,m,origin,dest;                     //结点数,边数
    int i,j;
    cin>>n>>m>>origin>>dest;
    bool visit[n] = {false};     //结点是否被访问过
    int map[n+1][n+1];           //邻接矩阵
    int pre[n+1];                //前缀数组
    int dis[n+1]={0};                //距离数组
    int weight[n+1]={0};             //点权数组
    int maxWeight[n+1]={0};


    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            map[i][j] = MAX;
    for(i=1;i<=n;i++)
        cin>>weight[i];
    for(i=0;i<m;i++)
    {
        int p,q,length;
        cin>>p>>q>>length;
        map[p][q] = length;
        map[q][p] = length;
    }

    for(i=1;i<=m;i++)
        dis[i] = map[origin][i];

    pre[origin] = -1;
    dis[origin] = 0;
    maxWeight[origin] = weight[origin];

    for(i=1;i<=n;i++)
    {
        int minLenth=MAX,minPos=-1;
        for(j=1;j<=n;j++)
        {
            if( !visit[j] && dis[j] < minLenth)
            {
                minLenth = dis[j];
                minPos = j;
            }
        }

        if(minPos == -1)
            break;
        visit[minPos] = true;
        for(j=1;j<=n;j++)
        {
            if(!visit[j] && map[minPos][j] != MAX)  //这里做一个判断,结点j不仅要属于V-s,同时在minPos与j之间必须有边
            {                                       //如果无边的话,肯定是没有必要更新距离的
                if(dis[j] > dis[minPos] + map[minPos][j])          //一条更短的路径
                {
                    pre[j] = minPos;
                    dis[j] = dis[minPos] + map[minPos][j];
                    maxWeight[j] = maxWeight[minPos] + weight[j];
                }
                else if(dis[j] == dis[minPos] + map[minPos][j] )    //新的最短路径
                {
                    if(maxWeight[j] < maxWeight[minPos] + weight[j])
                    {
                        maxWeight[j] = maxWeight[minPos] + weight[j];
                        pre[j] = minPos;
                    }
                }
            }
        }

    }

    cout<<maxWeight[dest]<<endl;
    findPath(dest,pre);

    return 0;
}

这里在更新最短路径时用到了一个判断,即结点j要与前结点有边。可知,如果无边的话就不必更新最短路径。

在只求最短距离时,用dist[j] > dist[minPos] + map[minPos][j]是足够判断的,因为无边则map[minPos][j]必定为无穷大,这个判断必然为假。但是这里需要判断dist[j] == dist[minPos] + map[minPos][j],而minPos有可能为起点,那么若起点与结点j无边的话,这个判断成立,而实际上无需更新。


问题2代码:

void findPath(int dest,int *p)
{
    if(p[dest] == -1)
    {
        cout<<dest;
        return ;

    }
    findPath(p[dest],p);
    cout<<"-"<<dest;
}
int main()
{
    int n,m,origin,dest;                     //结点数,边数
    int i,j;
    cin>>n>>m>>origin>>dest;
    bool visit[n] = {false};     //结点是否被访问过
    int map[n+1][n+1];           //邻接矩阵
    int pre[n+1];                //前缀数组
    int dis[n+1]={0};            //距离数组
    int cost[n+1][n+1]={0};      //边权数组
    int minCost[n+1]={0};        //最小花费数组


    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        {
            map[i][j] = MAX;
            cost[i][j] = MAX;
        }
    for(i=1;i<=m;i++)           //初始化边权
    {
        int p,q,weight;
        cin>>p>>q>>weight;
        cost[p][q] = weight;
        cost[q][p] = weight;
    }
    for(i=0;i<m;i++)            //初始化邻接矩阵
    {
        int p,q,length;
        cin>>p>>q>>length;
        map[p][q] = length;
        map[q][p] = length;
    }

    for(i=1;i<=m;i++)
    {
        dis[i] = map[origin][i];
        minCost[i] = MAX;
    }

    pre[origin] = -1;
    dis[origin] = 0;
    minCost[origin] = 0;

    for(i=1;i<=n;i++)
    {
        int minLenth=MAX,minPos=-1;
        for(j=1;j<=n;j++)
        {
            if( !visit[j] && dis[j] < minLenth)
            {
                minLenth = dis[j];
                minPos = j;
            }
        }

        if(minPos == -1)
            break;
        visit[minPos] = true;
        for(j=1;j<=n;j++)
        {
            if(!visit[j] && map[minPos][j] != MAX)          //这里做一个判断,结点j不仅要属于V-s,同时在minPos与j之间必须有边
            {                                               //如果无边的话,肯定是没有必要更新距离的
                if(dis[j] > dis[minPos] + map[minPos][j])           //一条更短的路径
                {
                    pre[j] = minPos;
                    dis[j] = dis[minPos] + map[minPos][j];
                    minCost[j] = minCost[minPos] + cost[minPos][j];
                }
                else if(dis[j] == dis[minPos] + map[minPos][j] )    //新的最短路径
                {
                    if(minCost[j] > minCost[minPos] + cost[minPos][j] )
                    {
                        minCost[j] = minCost[minPos] + cost[minPos][j];
                        pre[j] = minPos;
                    }
                }
            }
        }

    }

    cout<<minCost[dest]<<endl;
    findPath(dest,pre);

    return 0;
}

与第一个问题几乎没差,设一个cost[][]来储存一条边的权值,minCost[i]表示起点到节点i的最短路径上所花费的最短权值。

与第一个问题不同的是,

第一个问题里,maxWeight[]初始化全为0,而maxWeight[origin]为weight[origin],

而次问题中,minCost[]初始化为max,而minCost[origin]为0.

因为点权是求最大点权和,起点在更新节点i最短路径时,需要节点i的权值和小于两者相加,那么将结点i初始化为0是合理的,否则结点i将不小于加和,与起点直接相连的i无法更新。

同理,最小花费和需要结点i的花费小于加和,那么初始化为max是合理的。


问题3只需在判断时做修改,若是更短的路径,num[j] = num[minPos],如果是新的最短路径,则num[j] += num[minPos]; 


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值