很经典的最优路线问题
方法一:dfs
思路:
1.vector数组记录邻居关系用于深搜遍历
2.遍历过程中优化mindis_to[ ] ,fee[ ]
3.遍历过程中记录当前路径path
4.到达终点时优化最终答案
#include<iostream>
#include<vector>
using namespace std;
int N,M,S,D;
vector<int>v[500];
vector<int>path;
vector<int>final_path;
int final_dis=100000000,final_fee;
int dis[500][500];
int fee[500][500];
int mindis_to[500];
void dfs(int curnode,int curdis,int curfee)
{
if(curdis>mindis_to[curnode])return;//深搜第一步,不走冤枉路
path.emplace_back(curnode);
if(curnode==D)
{
if(curdis<final_dis||(curdis==final_dis&&curfee<final_fee))
{
final_dis=curdis;
final_path=path;
final_fee=curfee;
}
}
else
{
if(curdis<mindis_to[curnode])mindis_to[curnode]=curdis;
for(int i:v[curnode])dfs(i,curdis+dis[i][curnode],curfee+fee[i][curnode]);
}
path.pop_back();
}
int main()
{
int i,j,k,f;
cin>>N>>M>>S>>D;
while(M--)
{
cin>>i>>j>>k>>f;
v[i].emplace_back(j);
v[j].emplace_back(i);
dis[i][j]=dis[j][i]=k;
fee[i][j]=fee[j][i]=f;
}
for(i=0;i<N;i++)mindis_to[i]=100000000;
dfs(S,0,0);
for(int i:final_path)cout<<i<<' ';
cout<<final_dis<<' '<<final_fee;
}
优化mindis_to[ ]的作用
1.首先,可以防止走回头路导致的段错误。这一功能也可以dfs多携带一个参数记录上一个经过的结点,下次不走来实现。
...
...
void dfs(...,int pre)
{
...
...
for(int i:v[curnode])
{
if(i!=pre)dfs(...,curnode); //第一次调用时pre=-1
}
}
- 核心功能:剪枝,不走冤枉路
深搜遍历邻接表中“优化mindis_to[ ]”和 “使用vis[ ]标记走过点,只走未走过点的”的比较
- 二者都可以防止走回头路
- vis[]标记法保证每个结点都访问且只访问一次,但对任意节点只有一条路径。
mindis_to[ ]法走且仅走每一条必须“探索”的路,这些“探索”用于优化到每一结点的最短路径。 - 遍历树时,mindis_to[ ]法没有意义,因为每个结点都只有一个前驱。vis[ ]法效果等同于pre法
方法二:dijkstra
dijkstra : 以最小步伐探索黑暗
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXV = 510;
const int INF = 1000000000;
int n,m,st,ed;
int G[MAXV][MAXV],cost[MAXV][MAXV];
int mindis_to[MAXV],mincost_to[MAXV];
int pre[MAXV];
bool vis[MAXV]={false};
void dijkstra(int s)
{
fill(mindis_to,mindis_to+MAXV,INF);
fill(mincost_to,mincost_to+MAXV,INF);
for(int i=0;i<n;i++)pre[i]=i;
mindis_to[s]=0;
mincost_to[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 && mindis_to[j] < MIN)
{
u = j;
MIN = mindis_to[j];
}
}
if(u==-1)return; // 无点可标,完成任务
vis[u] = true; // 标记u
for(int v=0;v<n;v++) // 以u为中转站,试图更新mindis_to[v]
{
if(vis[v] == false && G[u][v] != INF) // v未访问且u可直接到达v
{
if(mindis_to[u] + G[u][v] < mindis_to[v] || (mindis_to[u]+G[u][v]==mindis_to[v] && mincost_to[v] > mincost_to[u] + cost[u][v]))
{
mindis_to[v] = mindis_to[u] + G[u][v];
mincost_to[v] = mincost_to[u] + cost[u][v];
pre[v] = u; // 记录前驱
}
}
}
}
}
void dfs(int v) // 打印路径
{
if(v == st)
{
cout<<v<<' ';
return;
}
dfs(pre[v]);
cout<<v<<' ';
}
int main()
{
int i,j,k,f;
cin>>n>>m>>st>>ed;
fill(G[0],G[0]+MAXV*MAXV,INF); // 初始化G
while(m--)
{
cin>>i>>j>>k>>f;
G[i][j]=G[j][i]=k;
cost[i][j]=cost[j][i]=f;
}
dijkstra(st);
dfs(ed);
cout<<mindis_to[ed]<<' '<<mincost_to[ed];
}