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];