PAT (Advanced Level) Practice 1111 Online Map Dijsktra加DFS

一、概述

求出起点到终点的最短路径和最快路径。

最短路径相同,选最快的,最快路径相同,选节点最少的。

首先要理解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<<" -> ";
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值