蓝桥备赛 3.30 4.1 Dijkstra算法和优先队列(堆优化)洛谷 P3371 【模板】单源最短路径(弱化版)P4779 【模板】单源最短路径(标准版)

3.30
由于前两天做到了有关BFS的最短路径问题,想起了上学期数据结构学过的Dijkstra和Floyd问题,所以今天下定决心看一看Dijkstra,话不多说直接上模板题
(今天依旧九点半起床)
P3371 【模板】单源最短路径(弱化版)
Dijkstra主要包含:

1、两个集合,一个是已经确定最短路径的点的集合s(初始只有起点),另一个则是未确定最短路径的点的集合u-s,我是用bool vis[ ]标记
2、一层大循环里面包含两层小循环:大循环是n个顶点的遍历,因为每一次遍历都确定顶点到某个点的最短路径,所以一共遍历顶点次数即可,接下来两层小循环分别是 找出u-s中顶点到其最短距离最近的点 和 每个点之间的松弛操作

接下来是第一次我用邻接矩阵描述图的代码,应该是多了很多用不着的空间,测试用例均是MLE错误代码如下所示:

#include<iostream>
#include<cstring> 
using namespace std;

#define INF 0x3f3f3f3f
int n,m,s;//分别表示点的个数、有向边的个数、出发点的编号
int u,v,w;//表示一条 u →v 的,长度为 w 的边
int arr[10005][10005]; 
int dis[10005];//dis[i]表示从起点到i点的最短距离 
bool vis[10005];//vis[i]=true表示已经确定从起点到i点的最短距离 

void Dijkstra(int start){
	vis[start]=true;
	dis[start]=0; 
	for(int i=1;i<=n;i++){//几个顶点即遍历多少遍,因为每遍历一遍就会确定顶点到某个点的最短路径 
		int min_dis=INF;
		int min_i=start;

		//找到下一个未确定最短路径并且是已标记路径中最短的那个点
		for(int j=1;j<=n;j++){
			if(vis[j]==false && dis[j]<min_dis){//有问题,都是INF 
				min_i=j;
				min_dis=dis[j];
			} 
		}		
		
		vis[min_i]=true;//标记min_i点已经确定最短路径
		 
		for(int j=1;j<=n;j++)//松弛操作 
			if(!vis[j] && dis[j]>dis[min_i]+arr[min_i][j])
				dis[j]=dis[min_i]+arr[min_i][j];
			
	} 
} 

int main(){
	cin>>n>>m>>s;//分别输入 点的个数、有向边的个数、出发点的编号
	memset(arr,INF,sizeof(arr));//将数组初始化无穷大 
	memset(dis,INF,sizeof(dis)); 
	while(m--){
		cin>>u>>v>>w;
		arr[u][v]=min(w,arr[u][v]);//有多条边 
	}
	Dijkstra(s);
	//输出答案 
	for(int i=1;i<n;i++)
		cout<<dis[i]<<" ";
	cout<<dis[n]<<endl;
	
	return 0;
}

后来考虑到用邻接表的数据结构,这里主要加深一下对 vector < edge > arr[100] 的理解

如果是int arr[100],那就是一个一维数组arr,最多含有100个 int 类型的元素
同理,vector < edge > arr[100]就是一个一维数组e,最多含有100个vector < edge > 类型的元素,而每一个vector又是一个一维向量,每一个这个向量都可以插入任意多个edge类型(即结构体)的元素,换句话说,vector < edge > arr[100] 就可以看成一个最多有100行的二维数组,而二维数组的列数是任意个,每一行的元素也不固定,这就保证了内存空间不会浪费

基于邻接表的普通Dijkstra代码如下:

#include<iostream>
#include<cstring>
#include<vector> 
#include<cmath>
using namespace std;

const int INF=pow(2,31)-1;
int n,m,s;//分别表示点的个数、有向边的个数、出发点的编号
int u,v,w;//表示一条 u →v 的,长度为 w 的边
int dis[10005];//dis[i]表示从起点到i点的最短距离 
bool vis[10005];//vis[i]=true表示已经确定从起点到i点的最短距离 

struct edge{
	int v;//下一个节点 
	int w;//权值 
};
vector<edge> arr[500005]; 

void Dijkstra(int start){
	vis[start]=true;
	dis[start]=0; 
	for(int i=1;i<=n;i++){//几个顶点即遍历多少遍,因为每遍历一遍就会确定顶点到某个点的最短路径 
		int min_dis=INF;
		int min_i=start;

		//找到下一个未确定最短路径并且是已标记路径中最短的那个点
		for(int j=1;j<=n;j++){
			if(vis[j]==false && dis[j]<min_dis){
				min_i=j;
				min_dis=dis[j];
			} 
		}		
		
		vis[min_i]=true;//标记min_i点已经确定最短路径
		 
		for(int j=0;j<arr[min_i].size();j++){//松弛操作 
			int next_node=arr[min_i][j].v;
			int weight=arr[min_i][j].w;
			dis[next_node]=min(dis[next_node],dis[min_i]+weight);
		}
	} 
} 

int main(){
	cin>>n>>m>>s;//分别输入:点的个数、有向边的个数、出发点的编号
	//memset(dis,INF,sizeof(dis));是不行的!!! 
	for(int i=0;i<=n;i++)
        dis[i]=INF;
	
	while(m--){
		cin>>u>>v>>w;
		arr[u].push_back({v,w});
	}
	Dijkstra(s);
	//输出答案 
	for(int i=1;i<n;i++)
		cout<<dis[i]<<" ";
	cout<<dis[n]<<endl;
	
	return 0;
}

参考P3371 【模板】单源最短路径(弱化版)(dijkstra模板题 )

这里要注意memset不能乱用!
初步了解了一下memset是按 字节 编码,如果这里使用memset(dis,INF,sizeof(dis));进行dis[ ]的初始化输出均为-1,debug了半天,具体后续再学习了解一下!

在Dijkstra算法的堆优化中,关键就在于优先队列的实现,这题尚且可以用普通的Dijkstra,但复杂度更高的就不行了,对应的进阶题:P4779 【模板】单源最短路径(标准版)
首先初步学习一下优先队列
ok,上模板题:P3378 【模板】堆
这道题非常简单,主要就考察了优先队列的定义,以及push( ),pop( ),top( )等简单应用
AC代码如下所示:

#include<iostream>
#include<queue>
using namespace std;

priority_queue<int,vector<int>,greater<int> > q; 
int op,n,num;

int main(){
	int n;
	cin>>n;
	while(n--){
		cin>>op;
		if(op==1){
			cin>>num;
			q.push(num);
		}
		if(op==2) cout<<q.top()<<endl;
		if(op==3) q.pop();
	}
	return 0;
}

完整定义:priority_queue<Type, Container, Functional>

Type 就是数据类型
Container 就是容器类型(Container必须是用数组实现的容器,STL里面默认用的是vector)Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型
默认是大顶堆,即从大到小排列

下面介绍两种常遇到的类型

一、当是单个整型元素时,定义priority_queue < int > q 即可,
这种定义等价于priority_queue< int ,vector< int> , less< int > >q
less默认从大到小排序,greater默认从小到大排序

二、当遇到结构体时,常常需要重载运算符,
bool operator < (const Node &T) const {
return a<T.a;//从大到小排序
}
小于号 “<” 是从大到小排,大于号 “>” 是从小到大排

这里我用了一小段代码测试了一下,如下所示:

#include<iostream>
#include<queue>
using namespace std;

struct Node{
	int a;
	int b;
	bool operator<(const Node &T)const{
		return a<T.a;//从大到小排序 
	}
};

priority_queue<Node> q;

int main(){
	Node aa,bb;
	cin>>aa.a>>aa.b>>bb.a>>bb.b;
	q.push(aa);
	q.push(bb);
	cout<<q.top().a<<" ";
	q.pop();
	cout<<q.top().a<<" ";

	return 0;
	
}

输出正确,结果按从大到小排序
Dijkstra的堆优化实现,后续更新~

3.31 周末懒惰了一下,(是的没错我就是很懒惰的人) ,早上云下午台球

4.1 愚人节快乐~~
话接上回,Dijkstra的堆优化中,主要省略了在 u-s 集合(即未确定最短路径的点的集合)中寻找路径最短的点的过程,其他就是一些优先队列的基本操作,AC代码如下所示:

#include<iostream>
#include<cstring>
#include<vector> 
#include<cmath>
#include<queue>
using namespace std;

const int INF=pow(2,31)-1;
int n,m,s;//分别表示点的个数、有向边的个数、出发点的编号
int u,v,w;//表示一条 u →v 的,长度为 w 的边
int dis[10005];//dis[i]表示从起点到i点的最短距离 
bool vis[10005];//vis[i]=true表示已经确定从起点到i点的最短距离

struct edge{
	int v;//下一个节点 
	int w;//权值 
	edge(int mm,int nn){
		v=mm; w=nn;
	}
	bool operator < (const edge &e) const{
		return w>e.w;//按权值从小到大排 
	}
}; 
priority_queue<edge> q;
vector<edge> arr[500005]; 

void Dijkstra(int start){
	q.push(edge(start,0));
	dis[start]=0;
	while(!q.empty()){
		edge p=q.top();
		q.pop();
		int min_i=p.v;
		if(vis[min_i]) continue;//说明vi点已经确认过最短路径 
		vis[min_i]=true;//找到了下一个未确定最短路径并且是已标记路径中最短的那个点,即是min_i 
		for(int j=0;j<arr[min_i].size();j++){//遍历所有min_i的邻边 
            if(vis[arr[min_i][j].v]) continue ;
			int next_node=arr[min_i][j].v;
			int weight=arr[min_i][j].w;
			dis[next_node]=min(dis[next_node],dis[min_i]+weight);
			q.push(edge(next_node,dis[next_node]));
		} 
	}
	
} 

int main(){
	cin>>n>>m>>s;//分别输入:点的个数、有向边的个数、出发点的编号
	//memset(dis,INF,sizeof(dis));是不行的!!! 
	for(int i=0;i<=n;i++)
        dis[i]=INF;
	
	while(m--){
		cin>>u>>v>>w;
		arr[u].push_back({v,w});
	}
	Dijkstra(s);
	//输出答案 
	for(int i=1;i<n;i++)
		cout<<dis[i]<<" ";
	cout<<dis[n]<<endl;
	
	return 0;
} 

至此over,接下来上标准版
P4779 【模板】单源最短路径(标准版)
相较于(弱化版)题中我用堆优化的解法,只需要改一下INF大小(改为1e9)和 dis[ ] 和 vis[ ] 数组大小即可,其他没区别,完整AC代码如下:

#include<iostream>
#include<cstring>
#include<vector> 
#include<cmath>
#include<queue>
using namespace std;

const int INF=1e9;
int n,m,s;//分别表示点的个数、有向边的个数、出发点的编号
int u,v,w;//表示一条 u →v 的,长度为 w 的边
int dis[100005];//dis[i]表示从起点到i点的最短距离 
bool vis[100005];//vis[i]=true表示已经确定从起点到i点的最短距离

struct edge{
	int v;//下一个节点 
	int w;//权值 
	edge(int mm,int nn){
		v=mm; w=nn;
	}
	bool operator < (const edge &e) const{
		return w>e.w;//按权值从小到大排 
	}
}; 
priority_queue<edge> q;
vector<edge> arr[200005]; 

void Dijkstra(int start){
	q.push(edge(start,0));
	dis[start]=0;
	while(!q.empty()){
		edge p=q.top();
		q.pop();
		int min_i=p.v;
		if(vis[min_i]) continue;//说明vi点已经确认过最短路径 
		vis[min_i]=true;//找到了下一个未确定最短路径并且是已标记路径中最短的那个点,即是min_i 
		for(int j=0;j<arr[min_i].size();j++){//遍历所有min_i的邻边 
            if(vis[arr[min_i][j].v]) continue ;
			int next_node=arr[min_i][j].v;
			int weight=arr[min_i][j].w;
			dis[next_node]=min(dis[next_node],dis[min_i]+weight);
			q.push(edge(next_node,dis[next_node]));
		} 
	}
	
} 

int main(){
	cin>>n>>m>>s;//分别输入:点的个数、有向边的个数、出发点的编号
	//memset(dis,INF,sizeof(dis));是不行的!!! 
	for(int i=0;i<=n;i++)
        dis[i]=INF;
	
	while(m--){
		cin>>u>>v>>w;
		arr[u].push_back({v,w});
	}
	Dijkstra(s);
	//输出答案 
	for(int i=1;i<n;i++)
		cout<<dis[i]<<" ";
	cout<<dis[n]<<endl;
	
	return 0;
} 

接下来复习Floyd算法咯…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Haoyu Xiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值