转载。 https://blog.csdn.net/henuwhr/article/details/79318730
多边权一般有三种
1 路径多条边权
2 增加点权
3 直接询问有多少条最短路
这就是Dijkstra的变形, 注意在这三种都是在最短路的基础上的,比如增加边权花费, 问在如果有多条最短路径情况下 最小花费是多少, 或者增加点权, 每个顶点都有一定物资,询问有多条最短路的情况下物资最多为多少, 还有就是直接问 最短路有多少条
一般解决这类问题的时候都会有三种不同的思路。
一种是在求最短的同时直接算出来题目所问的答案,另外一种就是利用Dijkstra求出来所有的最短路径之后再在最短路径中寻找符合题目要求的情况,通常后者是通过Dijkstra+DFS实现的。
先解释第一种解法
其实就是在原本的Dijkstra算法中修改更新d数组的代码即可
针对一条路径多权边 就拿路径除了距离之外的花费作为路径的第二标尺,用cost[u][v]表示u->v的花费,这个路径花费是由题目输入的,设置一个c数组,c[u]表示为从原点到u的最小花费,这个数组的作用对比于d数组来记忆。初始化时候c[s] = 0,其余各值均为INF。 所以就可以在d[v]>d[u]+G[u][v]的时候更新d[v]和c[v],与裸的Dijkstra所不同的是,在d[v]==d[u]+G[u][v]的时候,需要判断此时需不需要更新c[v],当c[v]>c[u]+cost[u][v]时,就更新c[v]。
具体看代码:
void Dijkstra(int v0){
memset(vis,false,sizeof(vis));
memset(d,INF,sizeof(d));
d[v0] = 0;
for(int i=0;i<n;i++){
int MIN = INF,u = -1;
for(int j=0;j<n;j++)
if(vis[j]==false&&d[j]>MIN){
MIN = d[j];
u = j;
}
if(u==-1) return;
vis[u] = true;
for(int v = 0;v < n;v++){
//如果v没有被访问&& u能够直接到达v
if(vis[v]==false&&G[u][v]!=INF){
if(d[v]>d[u]+G[u][v]){
d[v] = d[u] + G[u][v];
c[v] = c[u] + cost[u][v];
}
else if(d[v]==d[u]+G[u][v]&&c[v]>c[u]+cost[u][v]){
c[v] = c[u] + cost[u][v]; //最短距离相等时同时看能否使c[v]更优
}
}
}
}
}
针对增加点权的情况,同样的思考方式,weight[u]表示u这个城市的点权,就拿点权为城市的物举例,最终题目要求如果存在多条最短路的情况下求最多收集到的物资.。同样的增加一个w[]数组, w[u]表示从原点到u在最短路的基础上所收集的最大物资。初始化时,另w[s] = weight[s]其余均为0。 然后在d[v]>d[u]+G[u][v]的时候更新d[v]和w[v],在d[v]==d[u]+G[u][v]的时候判断是否能更新w[v],当w[v]<w[u]+weight[v]时,更新 w[v]。
代码如下:
void Dijkstra(int v0){
memset(vis,false,sizeof(vis));
memset(d,INF,sizeof(d));
memset(w,INF,sizeof(w));
d[v0] = 0;
w[v0] = weight[v0];
for(int i=0;i<n;i++){
int MIN = INF,u = -1;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<MIN){
MIN = d[j];
u = j;
}
}
if(u==-1)return;
vis[u] = true;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
if(d[v]>d[u]+G[u][v]){
d[v] = d[u]+G[u][v];
w[v] = w[u]+weight[v];
}
else if(d[v]==d[u]+G[u][v]&&w[v]<w[u]+weight[v]){
w[v] = w[u] + weight[v];
}
}
}
}
}
第三种情况
增加一个num[]数组,num[u]表示从原点v0到u的最短路径的条数。初始化时,num[s] = 0, 其余均为0。 在d[v]>d[u]+G[v][u]时,更新d[v]和用num[v]“继承”num[u]。当d[v]==d[u]+G[u][v](最短距离相等)时,将num[u]的加到num[v]上即可。
具体代码:
void Dijkstra(int v0){
memset(vis,false,sizeof(vis));
memset(d,INF,sizeof(d));
memset(num,0,sizeof(num));
d[v0] = 0;
num[v0] = 1;
for(int i=0;i<n;i++){
int MIN = INF, u = -1;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<MIN){
MIN = d[j];
u = j;
}
}
if(u==-1) return ;
vis[u] = true;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
if(d[v]>d[u]+G[u][v]){
d[v] = d[u] + G[u][v];
num[v] = num[u];
}
else if(d[v]==d[u]+G[u][v]){
num[v] += num[u];
}
}
}
}
}