再探Dijkstra
再探Dijkstra
Dij本质是存转移,而不是存点。以下图为例:

一开始,我们的优先队列中存储了 \(pq=\{(1\to ,w_3),(1\to ,w_1)\}\) 这两个转移,注意不是 \((3,w_2),(2,w_1)\) 这两个点,假设 \(w_1<w_2<w_3<w_4\),第一次取出 \((1\to,w_1)\) 这个转移,更新到 \(dis[2]=w_1\),然后遍历 \(2\) 的所有出边,将转移 \((2\to,dis[2]+w_4)\) 放入队列,现在队列中有 \(pq=\{(2\to,dis[2]+w_4),(1\to,w_3)\}\) 取出 \((1\to,w_3)\) 更新 \(dis[3]=w_3\),放入转移 \((3\to ,dis[3]+w_2)\),接着取出,更新 \(dis[4]=dis[3]+w_2\)。
总的来说,更新分为三步:
- 取出权值最小的转移。
- 确定转移所指的点的最短路。
- 遍历从这个点出发的转移并放入队列。
这样每个转移只会入队一次,复杂度 \(\mathcal O(m\log m)\)。
void dijkstra(){
	for(int i=1;i<=n;i++)
		dis[i]=INT_MAX;
	dis[s]=0;
	que.push({0,s});
	while(!que.empty()){ 
		node f=que.top();que.pop(); 
		if(vst[f.num])continue; 
		vst[f.num]=true; dis[f.num]=f.dis;
		for(int j=head[f.num];j;j=ed[j].next){
			if(vst[ed[j].v])continue;
			que.push({dis[f.num]+ed[j].w,ed[j].v}) ;
		}
	}
	return;
}
这样来看,一个转移不一定仅指向一个点,当指向多个点时,便有P5471 [NOI2019] 弹跳 - 洛谷。这时,为了保证一个点仅在确定的那个转移被遍历一次,我们需要用数据结构维护待确定集合。
⚠️注意,这并非数据结构优化建图,这仅仅是在维护剩余点集合⚠️
当只有点权没有边权时,也可以看作所有转移等价,这就是P7214 [JOISC 2020] 治療計画 - 洛谷

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号