《算法笔记》P378
emmm感觉从迪杰斯特拉开始,脑子就不够用了= =
输入样例:
第一行:顶点数、边数、起始顶点、结束顶点
5 6 0 2
第二行:每个顶点的点权(第几就是顶点几)
1 2 1 5 3
第3-3+边数行:两两顶点的边权
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
输出:起点到终点的最短路径条数、最短路径上的救援小组数目之后(如果有多条最短路径,则输出数目之和最大的)
2 4
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 510;
const int INF = 100000000;
//n为顶点数,m为边数,st、ed为起点、终点
//G为存储图的邻接矩阵,weight为点权
int n,m,st,ed,G[maxn][maxn],weight[maxn];
//d记录最短距离,w记录最大点权数之和,num记录最短路径条数
int d[maxn],w[maxn],num[maxn];
bool vis[maxn] = {false};
void Dijkstra(int s){
fill(d,d+maxn,INF);
memset(num,0,sizeof(num));
memset(w,0,sizeof(w));
d[s] = 0;
w[s] = weight[s];
num[s] = 1;
for(int i = 0;i <n;i++){
int u = -1,MIN = INF;
for(int j = 0;j <n;j++){
if(vis[j] == false && d[j]<MIN){
u = j;
MIN = d[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){
//&& 以u为中介能使d[v]更优
if(d[u]+G[u][v] <d[v]){
d[v] = d[u] + G[u][v]; //覆盖d[v]
w[v] = w[u] + weight[v]; //覆盖w[v]
num[v] = num[u]; //覆盖num[v]
}
else if(d[u] + G[u][v] == d[v]){ //找到一条相同长度的路径
if(w[u] + weight[v] >w[v]){ //以u为中介点时点权之和更大
w[v] = w[u] + weight[v]; //w[v]继承自w[u]
}
num[v] = num[v] + num[u]; //最短路径条数与点权无关,必须写在外面
}
}
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&st,&ed);
for(int i = 0;i <n;i++){
scanf("%d",&weight[i]);
}
int u,v;
fill(G[0],G[0]+maxn*maxn,INF);
for(int i = 0;i <m;i++){
scanf("%d%d",&u,&v);
scanf("%d",&G[u][v]);
G[v][u] = G[u][v];
}
Dijkstra(st);
printf("%d %d\n",num[ed],w[ed]);
return 0;
}
/*
5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
*/
若在以上问题基础上,要求输出最短路径的前驱节点如何处理?
(迪杰斯特拉部分代码根据书上写的,DFS部分自己写的,但似乎对于多个最短路径的时候遍历并不好。。)
代码:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 510;
const int INF = 1000000000;
vector<int> pre[maxn];
int n,m,st,ed,G[maxn][maxn],weight[maxn];
int d[maxn];
bool vis[maxn] = {false};
void Dijsktra(int s){
fill(d,d+maxn,INF);
d[s] = 0;
for(int i = 0;i <n;i++){
int u = -1,MIN = INF;
for(int j = 0;j <n;j++){
if(vis[j] == false && d[j] <MIN){
u = j;
MIN = d[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[u] + G[u][v] <d[v]){
d[v] = d[u] + G[u][v];
pre[v].clear();
pre[v].push_back(u);
}
else if(d[u] + G[u][v] == d[v]){
pre[v].push_back(u);
}
}
}
}
}
void DFS(int s){
if(s == st){
for(int i = 0;i <pre[st].size();i++){
printf("%d ",pre[st][i]);
}
}
else{
for(int j = 0;j <pre[s].size();j++){
printf("%d ",pre[s][j]);
DFS(pre[s][j]);
}
}
}
int main(){
scanf("%d %d %d %d",&n,&m,&st,&ed);
for(int i = 0;i <n;i++){
scanf("%d",&weight[i]);
}
int u,v;
fill(G[0],G[0]+maxn*maxn,INF);
for(int i = 0;i <m;i++){
scanf("%d %d",&u,&v);
scanf("%d",&G[u][v]);
G[v][u] = G[u][v];
}
Dijsktra(st);
for(int i = 1;i <n;i++){
printf("%d\n",d[i]); //每个顶点的最短距离
}
DFS(ed); //输出到终点最短路径前驱的顶点
}
/*
5 6 0 4
1 2 1 5 3
0 1 1
0 2 4
0 3 5
1 2 1
2 4 1
3 4 1
*/
《算法笔记》P384,对于有第二标尺的情况下,DFS选择第二标尺最优的路径来输出:
伪代码如下:
int optvalue; //第二标尺最优值
vector<int>path,tempath; //存放最优路径,临时路径
void DFS(int v){
//递归边界
if(v == st){
tempath.push_back(v);
int value;
计算路径tempath上的value值;
if(value优于optvalue){
optvalue = value;
path = tempath;
}
tempath.pop_back();
return;
}
tempath.push_back(v);
for(int i = 0;i <pre[v].size();i++){
DFS(pre[v][i]); //节点v的前驱节点pre[v][i]递归
}
tempath.pop_back(); //遍历完所有前驱节点,将当前节点v删除
}
如下是完整实现代码(末行输出最短路径上的顶点,倒序输出,是从起点到终点的顺序;并且有相同最短路径时,根据第二标尺点权之和最优来输出)
有几点要注意:
1、path可变数组、tempath临时可变数组
2、由于递归的原因,存放在tempath中的路径节点是逆序的,因此访问节点需要倒着进行(这也是DFS之中和最后输出的时候倒序访问的原因)
代码:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 510;
const int INF = 1000000000;
vector<int> pre[maxn];
int n,m,st,ed,G[maxn][maxn],weight[maxn];
int d[maxn];
bool vis[maxn] = {false};
int optvalue = 0; //第二标尺最优值
vector<int>path,tempath; //存放最优路径,临时路径
void Dijsktra(int s){
fill(d,d+maxn,INF);
d[s] = 0;
for(int i = 0;i <n;i++){
int u = -1,MIN = INF;
for(int j = 0;j <n;j++){
if(vis[j] == false && d[j] <MIN){
u = j;
MIN = d[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[u] + G[u][v] <d[v]){
d[v] = d[u] + G[u][v];
pre[v].clear();
pre[v].push_back(u);
}
else if(d[u] + G[u][v] == d[v]){
pre[v].push_back(u);
}
}
}
}
}
void DFS(int v){
//递归边界
if(v == st){
tempath.push_back(v);
/* 边权之和
int value = 0;
for(int i = tempath.size() - 1;i >0;i--){
int id = tempath[i],idNEXT = tempath[i-1];
value += V[id][idNEXT];
}
*/
//点权之和
int value = 0;
for(int i = tempath.size() - 1;i>= 0;i--){
int id = tempath[i];
value += weight[id];
}
if(value>optvalue){
optvalue = value;
path = tempath;
}
tempath.pop_back();
return;
}
tempath.push_back(v);
for(int i = 0;i <pre[v].size();i++){
DFS(pre[v][i]); //节点v的前驱节点pre[v][i]递归
}
tempath.pop_back(); //遍历完所有前驱节点,将当前节点v删除
}
int main(){
scanf("%d %d %d %d",&n,&m,&st,&ed);
for(int i = 0;i <n;i++){
scanf("%d",&weight[i]);
}
int u,v;
fill(G[0],G[0]+maxn*maxn,INF);
for(int i = 0;i <m;i++){
scanf("%d %d",&u,&v);
scanf("%d",&G[u][v]);
G[v][u] = G[u][v];
}
Dijsktra(st);
for(int i = 1;i <n;i++){
printf("%d\n",d[i]); //每个顶点的最短距离
}
DFS(ed); //输出到终点最短路径前驱的顶点
for(int i = path.size() - 1;i >= 0;i--){ //倒序输出路径上的点
printf("%d ",path[i]);
}
}
/*
5 6 0 4
1 2 1 5 3
0 1 1
0 2 4
0 3 5
1 2 1
2 4 1
3 4 1
*/