一、概述
求出起点到终点的最短路径和最快路径。
最短路径相同,选最快的,最快路径相同,选节点最少的。
首先要理解oneway指的是单向路。然后才能往下做。
第一眼就看出来了是用Dijsktra加DFS,但是最开始还以为求节点最少要用BFS,后来才发现还得用DFS,因为BFS没法保存路径。
剩下的就是代码实现了。
因不熟练,所以debug加上思考还花了一点时间。实际上算法写的熟练了可以一小时内写完的。
二、分析
图中每个边有五个属性:两个端点,是否单行加上距离,时间。
因此这是一个有向图。同时注意一点,DFS时是从终点往起点DFS的,那么考虑到DFS需要时间这一属性,加上是有向图,因此时间矩阵要有两个。区别在于,如果路径只能沿i到j,花费时间t;那么正时间矩阵的和逆时间矩阵的为t的下标应该是相反的。否则会出错。
然后开始输入。因为是图,所以开三个邻接矩阵并初始化,注意初始化为INF。然后输入。这很机械,应该不假思索的写出。如下:
fill(dis[0],dis[0]+510*510,INF);
fill(Time[0],Time[0]+510*510,INF);
fill(Timeover[0],Timeover[0]+510*510,INF);
scanf("%d %d",&N,&M);
for(int i=0;i<M;i++)
{
int V1,V2,oneway;
float L,T;
scanf("%d %d %d %f %f",&V1,&V2,&oneway,&L,&T);
if(oneway==1)
{
dis[V1][V2]=L;
Time[V1][V2]=T;
Timeover[V2][V1]=T;
}
else
{
dis[V1][V2]=L;
Time[V1][V2]=T;
dis[V2][V1]=L;
Time[V2][V1]=T;
Timeover[V2][V1]=T;
Timeover[V1][V2]=T;
}
}
以后还要更熟练。
如果决定使用Dijsktra,那么直接开两个数组,min和vis,如果需要Dijsktra保存路径,再直接开一个pre。然后按照Dijsktra的套路写出:
int near[510];
int nearvis[510]={0};
void Dijkstra_D(int root)
{
fill(near,near+510,INF);
near[root]=0;
int now=0;
while(now!=N)
{
int min=INF;
int nummin=-1;
for(int i=0;i<N;i++)
{
if(near[i]<min&&nearvis[i]==0)
{
min=near[i];
nummin=i;
}
}
if(nummin==-1)
return;
nearvis[nummin]=1;
now++;
for(int j=0;j<N;j++)
{
if(dis[nummin][j]<INF)
{
if(dis[nummin][j]+near[nummin]<near[j])
{
pre_D[j].clear();
pre_D[j].push_back(nummin);
near[j]=dis[nummin][j]+near[nummin];
}
else if(dis[nummin][j]+near[nummin]==near[j])
{
pre_D[j].push_back(nummin);
}
}
}
}
}
这期间还有一些迟疑的地方,很不应该,现在再次明确一下:
最小值数组初始化为INF,vis数组初始化为0;
在进循环之前先初始化,并且让入口的最小值为0;
循环条件为已确定数量等于节点数量;
进入循环后首先找到最小值数组中未访问过的最小值,令其vis等于1;
然后访问这一行,找所有不是INF的节点;
对于找到新的路径的,三句话:
清pre;加pre;改min;
对于找到同样路径的,一句话:
加pre;
然后开始DFS。如果确定使用DFS找路径,开两个向量和一个最值。
与Dijsktra配合的DFS与普通的DFS不同,这个是要找最佳路径的,因此需要维护一个最大值或最小值。
别的DFS都是递归出口加递归体,这个更死板,如下:
int fastest=INF;
vector<int> temp;
vector<int> fastpath;
void DFS(int root,int Timesum)
{
if(pre_D[root].empty())
{
temp.push_back(root);
if(Timesum<fastest)
{
fastest=Timesum;
fastpath=temp;
}
temp.pop_back();
}
else
{
temp.push_back(root);
vector<int>::iterator it;
for(it=pre_D[root].begin();it!=pre_D[root].end();it++)
{
DFS(*it,Timesum+Timeover[root][*it]);
}
temp.pop_back();
}
}
先判断是不是叶节点,用pre的empty;
如果是:
压入路径,比较大小,更新最佳路径,弹出路径;
如果不是:
压入路径,遍历其pre,DFS,遍历后弹出。
因为这种DFS一般都是有第二个条件,需要让其最优的,需要在DFS中加一个参数,即目前该条件的值,并在递归中不断更新。
接下来是另一个Dijkstra和另一个DFS了,区别不大。
最后输出即可。
三、总结
写出正确的Dijsktra和DFS对我来说不难,难的是快速写出不出bug。这还要练习。
PS:代码如下:
#include<stdio.h>
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<vector>
#include<math.h>
#include<algorithm>
#include<queue>
using namespace std;
int INF=0x3f3f3f3f;
//首先找出距离最短的,并且维护前驱数组
//然后DFS前驱数组找出最快的
//然后找出最快的
//最后DFS找出层数最小的
int N,M,S,D;//节点数,街道数
int dis[510][510];
int Time[510][510];
int Timeover[510][510];
vector<int> pre_D[510];
vector<int> pre_T[510];
int fast[510];
int fastvis[510]={0};
int near[510];
int nearvis[510]={0};
void Dijkstra_D(int root)
{
fill(near,near+510,INF);
near[root]=0;
int now=0;
while(now!=N)
{
int min=INF;
int nummin=-1;
for(int i=0;i<N;i++)
{
if(near[i]<min&&nearvis[i]==0)
{
min=near[i];
nummin=i;
}
}
if(nummin==-1)
return;
nearvis[nummin]=1;
now++;
for(int j=0;j<N;j++)
{
if(dis[nummin][j]<INF)
{
if(dis[nummin][j]+near[nummin]<near[j])
{
pre_D[j].clear();
pre_D[j].push_back(nummin);
near[j]=dis[nummin][j]+near[nummin];
}
else if(dis[nummin][j]+near[nummin]==near[j])
{
pre_D[j].push_back(nummin);
}
}
}
}
}
void Dijkstra_T(int root)
{
fill(fast,fast+510,INF);
fast[root]=0;
int now=0;
while(now!=N)
{
int min=INF;
int nummin=-1;
for(int i=0;i<N;i++)
{
if(fast[i]<min&&fastvis[i]==0)
{
min=fast[i];
nummin=i;
}
}
if(nummin==-1)
return;
fastvis[nummin]=1;
now++;
for(int j=0;j<N;j++)
{
if(Time[nummin][j]<INF)
{
if(Time[nummin][j]+fast[nummin]<fast[j])
{
pre_T[j].clear();
pre_T[j].push_back(nummin);
fast[j]=Time[nummin][j]+fast[nummin];
}
else if(Time[nummin][j]+fast[nummin]==fast[j])
{
pre_T[j].push_back(nummin);
}
}
}
}
}
int fastest=INF;
vector<int> temp;
vector<int> fastpath;
void DFS(int root,int Timesum)
{
if(pre_D[root].empty())
{
temp.push_back(root);
if(Timesum<fastest)
{
fastest=Timesum;
fastpath=temp;
}
temp.pop_back();
}
else
{
temp.push_back(root);
vector<int>::iterator it;
for(it=pre_D[root].begin();it!=pre_D[root].end();it++)
{
DFS(*it,Timesum+Timeover[root][*it]);
}
temp.pop_back();
}
}
int lest=INF;
vector<int> Timetemp;
vector<int> lesspath;
void DFS_level(int root,int levelsum)
{
if(pre_T[root].empty())
{
Timetemp.push_back(root);
if(levelsum<lest)
{
lest=levelsum;
lesspath=Timetemp;
}
Timetemp.pop_back();
}
else
{
Timetemp.push_back(root);
vector<int>::iterator it;
for(it=pre_T[root].begin();it!=pre_T[root].end();it++)
{
DFS_level(*it,levelsum+1);
}
Timetemp.pop_back();
}
}
int main()
{
fill(dis[0],dis[0]+510*510,INF);
fill(Time[0],Time[0]+510*510,INF);
fill(Timeover[0],Timeover[0]+510*510,INF);
scanf("%d %d",&N,&M);
for(int i=0;i<M;i++)
{
int V1,V2,oneway;
float L,T;
scanf("%d %d %d %f %f",&V1,&V2,&oneway,&L,&T);
if(oneway==1)
{
dis[V1][V2]=L;
Time[V1][V2]=T;
Timeover[V2][V1]=T;
}
else
{
dis[V1][V2]=L;
Time[V1][V2]=T;
dis[V2][V1]=L;
Time[V2][V1]=T;
Timeover[V2][V1]=T;
Timeover[V1][V2]=T;
}
}
scanf("%d %d",&S,&D);
Dijkstra_D(S);
DFS(D,0);
Dijkstra_T(S);
DFS_level(D,0);
vector<int>::reverse_iterator it;
cout<<"Distance = "<<near[D];
if(fastpath!=lesspath)
{
cout<<": ";
for(it=fastpath.rbegin();it!=fastpath.rend();it++)
{
cout<<*it;
if(it!=fastpath.rend()-1)
cout<<" -> ";
}
cout<<'\n';
}
else
{
cout<<"; ";
}
cout<<"Time = "<<fast[D]<<": ";
for(it=lesspath.rbegin();it!=lesspath.rend();it++)
{
cout<<*it;
if(it!=lesspath.rend()-1)
cout<<" -> ";
}
}