算法每周一题(一)——单源最短路


原题目


题目描述
给定一个 \(n\) 个点,\(m\) 条有向边的带非负权图,请你计算从 \(s\) 出发,到每个点的距离。

数据保证能从 \(s\) 出发到任意点。

输入格式
第一行为三个正整数 \(n, m, s\)。 第二行起 \(m\) 行,每行三个非负整数 \(u_i, v_i, w_i\),表示从 \(u_i\)\(v_i\) 有一条权值为 \(w_i\) 的有向边。

输出格式
输出一行 \(n\) 个空格分隔的非负整数,表示 \(s\) 到每个点的距离。


初次尝试:


由于刚刚学过数据结构,很快就有了思路(采用邻接表存储+\(Dijkstra\)算法),但是由于从没实践过??,所以写的时候有点费劲,又调试了好久,才得到正确的结果:

  • 第一代代码:
//单源最短路传统
#include
using namespace std;
int dist[100005],path[100005];
bool S[100005];
typedef struct e{
	int key;
	int d;
	struct e *next;
}edge;
typedef struct{
	edge *out=NULL;
}node;
void add(node *n,int v,int w)
{
	edge *e=(edge *)malloc(sizeof(edge));
	e->key=v;
	e->d=w;
	e->next=n->out;
	n->out=e;
}
void kl(node *n,int k)
{
	edge *e=n->out;
	while(e!=NULL)
	{
		if(e->d+dist[k]key])
		{
			dist[e->key]=e->d+dist[k];
			path[e->key]=k;
		}
		e=e->next;
	}
}
int main()
{
	memset(dist,100,sizeof(dist));//距离初始化为无穷,这里为一个很大的数
	memset(path,-1,sizeof(path));
	int n,m,s,u,v,w;	
	node N[100005];
	scanf("%d%d%d",&n,&m,&s);
	int nn=n;
	dist[s]=0;path[s]=s;dist[0]=2000000005;
	for(int i=0;i

痛苦地写了很久很久,结果全部超时了,简直太悲伤了!!????

又审视写下的代码,发现while(nn!=0)内存在两个循环,一个在kl()函数中,但遍历所有点是算法要求因此不作优化;另一个“寻找距离最短的结点”时间复杂度为O(n),需要考虑对其进行优化。由于总是取出距离最短的结点,想到可以使用堆来进行维护,时间复杂度为O(1),但此时笔者已经心太累只想快点完成这道题,因此直接使用STL库中定义好的优先队列——堆来进行优化。

  • 小资料

    • stl优先队列的使用:

    • //定义一个优先队列
      std::priority_queue q;
      //数据操作
      q.push()//入队
      q.pop()//出队
      q.top()//返回顶部元素
      q.empty()//返回队列是否为空
      /*特别地,stl优先队列使用<进行大小比较,对于自定义的容器,需要在内部重载运算符才能正常使用。*/
      //使用范例
      #include
      using namespace std;
      struct node{
      	int k;
      	int	m;
      	bool operator <(const node &x)const{
      		return k q;
      	q.push({1,2});
      	q.push({2,1});
      	q.push({0,7});
      	node temp=q.top();
      	cout<

解法优化


优化的关键在于想到把新旧的<距离,结点>组全部放在一个堆中是没有关系的,只要取出以后判断结点是否已经访问过即可。这样就方便多了。其次本题中path[]数组也是没有用的,直接注释掉。

  • 优化后代码
//单源最短路堆优化
#include
using namespace std;
int dist[100005];//int path[100005];//本题path[]数组好像没什么用 
bool vis[100005];
//定义数据结构
struct edge{
	int key;
	int d;
	edge *next;
};
struct node{
	edge *out=NULL;
}N[100005];
struct node_dist{
	int dist;
	int node;
	bool operator < (const node_dist &x)const
	{
		return dist>x.dist; 
	}//重载运算符使兼容,并定义成小根堆
};
std::priority_queue q;//使用stl库定义一个优先队列
//定义函数
void add(node *n,int v,int w)
{
	edge *e=(edge *)malloc(sizeof(edge));
	e->key=v;
	e->d=w;
	e->next=n->out;
	n->out=e;
}

void kaolv(node *n,int k)
{
	edge *e=n->out;
	while(e!=NULL)
	{
		if(e->d+dist[k]key])
		{
			dist[e->key]=e->d+dist[k];
//			path[e->key]=k;
			q.push({dist[e->key],e->key});
		}
		e=e->next;
	}
}

int main()
{
	int n,m,s,u,v,w; 
	memset(dist,100,sizeof(dist));//将dist[]数组所有元素初始化为无穷(很大的值)
//	memset(path,-1,sizeof(path));	
	scanf("%d %d %d",&n,&m,&s);
	int nn=n;
	//输入 
	for(int i=0;i


终于过了,记录下成功的喜悦2021-8-4 凌晨1点24分!!