这道题就当作dij+dfs练习用的,需要特别留心的就是各个数组、临时变量的初始化问题,不要总是debug的时候才发现是某个地方没有及时初始化导致出错,比如dst数组先初始化为INF,再将dis[src]设置为0,开始dij算法,dfs的时候,记录第二尺度最优的变量也要记得初始化。
代码:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define maxNode 505
#define INF 0x3f3f3f3f
int G[maxNode][maxNode];
int costs[maxNode][maxNode]={0};
bool vis[maxNode]={false};
int dis[maxNode];
vector<int> pre[maxNode];//无需初始化
int optValue=INF;
vector<int> path,tmpPath;//存储最短路径
void dij(int src,int N){
for(int i=0;i<N;i++)
dis[i]=INF;
dis[src]=0;
for(int i=0;i<N;i++){//跑N次dij
int v=-1,tmpMin=INF;
for(int w=0;w<N;w++){//从未收录顶点中找dis最小的进行收录
if(!vis[w] && dis[w]<tmpMin){
v=w;
tmpMin=dis[w];
}
}
if(v==-1) return;//说明图不连通
vis[v]=true;//将v收录,并更新邻接点的dst
for(int w=0;w<N;w++){
//cout << G[v][w] << " " << dis[v]+G[v][w] << " " << dis[w] << endl;
if(!vis[w] && G[v][w]!=INF && dis[v]+G[v][w]<dis[w]){
dis[w]=dis[v]+G[v][w];
pre[w].clear();
pre[w].push_back(v);
}else if(!vis[w] && G[v][w]!=INF && dis[v]+G[v][w]==dis[w]){
pre[w].push_back(v);
}
}
}
}
void dfs(int v,int src){
//递归边界
if(v==src){
tmpPath.push_back(v);//此时起点要记得push进去
//计算第二尺度
int cost=0;
for(int i=tmpPath.size()-1;i>0;i--){
cost+=costs[tmpPath[i]][tmpPath[i-1]];
}
if(cost<optValue){
optValue=cost;
path=tmpPath;
}
tmpPath.pop_back();
return;
}
//递归式
tmpPath.push_back(v);
for(int i=0;i<pre[v].size();i++){
dfs(pre[v][i],src);
}
tmpPath.pop_back();
}
int main() {
#ifdef ONLINE_JUDGE
#else
freopen("1.txt","r",stdin);
#endif
int N,M,src,dst;
cin >> N >> M >> src >> dst;
//初始化图
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(i==j) G[i][j]=0;
else G[i][j]=INF;
}
}
//建图
for(int i=0;i<M;i++){
int v,w,dis,cost;
cin >> v >> w >> dis >> cost;
G[v][w]=dis;
G[w][v]=dis;
costs[v][w]=cost;
costs[w][v]=cost;
}
//跑dij
dij(src,N);
//然后进行dfs选择第二尺度最优的
tmpPath.push_back(dst);//先把终点push进去
for(int i=0;i<pre[dst].size();i++){
dfs(pre[dst][i],src);//递归边界是到访问到起点
}
for(int i=path.size()-1;i>=0;i--){
cout << path[i] << " ";
}
cout << dis[dst] << " " << optValue;
return 0;
}
·
·
·
·
·
·
另一种回溯方式是在for循环中先push进去v,再dfs(v),再pop出来v,这样在递归边界就不用专门把最后一个顶点在push pop的操作了。
代码如下:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define maxNode 505
#define INF 0x3f3f3f3f
int G[maxNode][maxNode];
int costs[maxNode][maxNode]={0};
bool vis[maxNode]={false};
int dis[maxNode];
vector<int> pre[maxNode];//无需初始化
int optValue=INF;
vector<int> path,tmpPath;//存储最短路径
void dij(int src,int N){
for(int i=0;i<N;i++)
dis[i]=INF;
dis[src]=0;
for(int i=0;i<N;i++){//跑N次dij
int v=-1,tmpMin=INF;
for(int w=0;w<N;w++){//从未收录顶点中找dis最小的进行收录
if(!vis[w] && dis[w]<tmpMin){
v=w;
tmpMin=dis[w];
}
}
if(v==-1) return;//说明图不连通
vis[v]=true;//将v收录,并更新邻接点的dst
for(int w=0;w<N;w++){
//cout << G[v][w] << " " << dis[v]+G[v][w] << " " << dis[w] << endl;
if(!vis[w] && G[v][w]!=INF && dis[v]+G[v][w]<dis[w]){
dis[w]=dis[v]+G[v][w];
pre[w].clear();
pre[w].push_back(v);
}else if(!vis[w] && G[v][w]!=INF && dis[v]+G[v][w]==dis[w]){
pre[w].push_back(v);
}
}
}
}
void dfs(int v,int src){
//递归边界
if(v==src){
//tmpPath.push_back(v);
//计算第二尺度
int cost=0;
for(int i=tmpPath.size()-1;i>0;i--){
cost+=costs[tmpPath[i]][tmpPath[i-1]];
}
if(cost<optValue){
optValue=cost;
path=tmpPath;
}
//tmpPath.pop_back();
return;
}
//递归式
//tmpPath.push_back(v);
for(int i=0;i<pre[v].size();i++){
tmpPath.push_back(pre[v][i]);
dfs(pre[v][i],src);
tmpPath.pop_back();
}
//tmpPath.pop_back();
}
int main() {
#ifdef ONLINE_JUDGE
#else
freopen("1.txt","r",stdin);
#endif
int N,M,src,dst;
cin >> N >> M >> src >> dst;
//初始化图
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(i==j) G[i][j]=0;
else G[i][j]=INF;
}
}
//建图
for(int i=0;i<M;i++){
int v,w,dis,cost;
cin >> v >> w >> dis >> cost;
G[v][w]=dis;
G[w][v]=dis;
costs[v][w]=cost;
costs[w][v]=cost;
}
//跑dij
dij(src,N);
//然后进行dfs选择第二尺度最优的
tmpPath.push_back(dst);//先把终点push进去
//也可以放在循环里面,先push再dfs再pop出来回溯
for(int i=0;i<pre[dst].size();i++){
tmpPath.push_back(pre[dst][i]);
dfs(pre[dst][i],src);//递归边界是到访问到起点
tmpPath.pop_back();
}
for(int i=path.size()-1;i>=0;i--){
cout << path[i] << " ";
}
cout << dis[dst] << " " << optValue;
return 0;
}
其实下面这一段(88到97行)也是可以简化的,因为下面这个for循环(93-97行)在dfs中也有:
//跑dij
dij(src,N);
//然后进行dfs选择第二尺度最优的
tmpPath.push_back(dst);//先把终点push进去
//也可以放在循环里面,先push再dfs再pop出来回溯
for(int i=0;i<pre[dst].size();i++){
tmpPath.push_back(pre[dst][i]);
dfs(pre[dst][i],src);//递归边界是到访问到起点
tmpPath.pop_back();
}
因此可以直接写成如下方式:
//跑dij
dij(src,N);
//然后进行dfs选择第二尺度最优的
tmpPath.push_back(dst);//先把终点push进去
dfs(dst,src);
感觉后面这种写法更好,就尽量统一起来,后者好在如果src和dst相同,也能进入到dfs中,在递归边界中把存储最优路径的path更新掉,如果是上一种方式,pre[dst].size()将为0,for循环不会执行,也就没有进入递归,path不会更新,不过题目应该一般不会有起点和终点一样的情况。