dij告诉你拿金的路

dij告诉你拿金最短的路

dijk求最小路

这是我的一个接触的最短路算法,在之前看到了SPFA已死的多种言论之后我决定想去学dij,这个算法就类似于SPFA加了一个堆的优化,有一点贪心的思想在里面,其实本质上还是松弛,我们假设要找的点是x,那么我们先去找x所有的路,去更新dis,(注意dis最开始赋值为无穷大),之后入堆,我们每次拿出堆顶的的一个去松弛她所连接的点,之后知道所有都完成为止,其实不难,注意这个不可以去求有负环的最短路,如果有就还是用已经死掉(bushi)的SPFA
最后模板来一手

int dis[10000]; 
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;//造一个小根堆,另外一个存是那个点 
void dij(int x){
	for(int i=1;i<=n;i++)dis[i]=oo;
	dis[x]=0;
	q.push(pair<int,int>(dis[x],x));
	while(!q.empty()){
		while(!q.empty()&&q.top().first>dis[q.top().second]) q.pop();//如果堆里面的这个被其他的给松弛了,那就出堆,没用了
		if(q.empty()) break;//这个加上比较快
		
		int u=q.top().second;
		dis[u]=q.top().first;//只有出堆的时候才确定数值
		q.pop(); 
		for(int i=head[u];i>0;i=nex[i]){
			if(dis[u]+val[i]<dis[to[i]]){
				dis[to[i]]=dis[u]+val[i];
				q.push(pair<int ,int>(dis[to[i]],to[i]) );
			}
			
		}
	}
	//printf("%d",dis[4]);
	
}

其实也可以加一个vis数组去更好的理解,但其实没有用,这里加上一个例题
https://www.luogu.com.cn/problem/P1144
最短路计数,用bfs也可以,但要是边权不在是1,那就只能用dij了,首先先去想一想dij的过程:找点。松弛。入堆。。。。,那么我们在什么地方会有机会判断是否以及被又来过一次呢,那就是在松弛的时候,我们知道假如说dis[g]+val[i]=dis[to[i]]那么这个点是一定之前来过,并且这个dis一定是已经确定的最短路,这时候只要我们发现这个点又来了一边最短路,那么根据加法原理(先不考虑重边)cnt[to[i]]=cnt[g]+cnt[to[i]](cnt是积累最短路数的数组),那假如说这个点将要被松弛,这个点的最短路到达次数是不是就是到达这个点的最短路个数,即cut[to[i]=cnt[g],这就是这道题的一个朴素的想法,至于自环其实和dij没啥关系,也不会去拿自己松弛自己;还有重边,其实也没关系,就是再被到达一次嘛,这里就可以理解为用了乘法原理,但其实加法原理也可以想的通
代码如下

#include<bits/stdc++.h>
using namespace std;
#define pp 2000005
#define ppp 1000005
#define mmm 100003
int head[pp],to[pp],nex[pp],val[pp];
int num=0;
void add(int x,int y,int z){
	to[++num]=y;
	nex[num]=head[x];
	head[x]=num;
	val[num]=z;
}



int n,m;
void get_input(){
	scanf("%d%d",&n,&m);
	int a,b;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&a,&b);
		add(a,b,1);
		add(b,a,1);
	}
}

int dis[ppp];
long long cnt[ppp];
bool vis[ppp];
priority_queue<pair<int,int> ,vector<pair<int,int> >,greater<pair<int,int> > >q; 

void dij(int x){
	for(int i=1;i<=n;i++) dis[i]=pp;
	dis[x]=0;
	vis[x]=true;
	q.push(pair<int,int>(dis[x],x));
	cnt[1]=1;
	while(!q.empty()){
		while(!q.empty()&&q.top().first>dis[q.top().second]) q.pop();
		if(q.empty())return;
		
		int g=q.top().second;
		dis[g]=q.top().first;
		vis[g]=true;
		q.pop();
		
		for(int i=head[g];i>0;i=nex[i]){
			if(dis[g]+val[i]==dis[to[i]]){
				cnt[to[i]]+=cnt[g];
				cnt[to[i]]%=mmm;
			}
			if(!vis[to[i]]&&dis[g]+val[i]<dis[to[i]]){
				dis[to[i]]=dis[g]+val[i];
				cnt[to[i]]=cnt[g];
				q.push(pair<int,int>(dis[to[i]],to[i]));
			}
		}
	}
}

void get_ans(){
	for(int i=1;i<=n;i++){
		printf("%lld\n",cnt[i]);
	}
}

int main(){
	get_input();
	dij(1);
	get_ans();
	
	
	return 0;
}

杂题

一P1629 邮递员送信

这是一个最短路裸,但是我们发现邮递员取得时候是1到搜索所有点的最小距离,但是回来时不一样了,是多个点到1的最小距离,于是我们想起了多元最短路,但是发现时间绝对会炸,所以考虑一下

去的时候是1到多,回来时可不可以也可以1到多呢
可以

只要我们那原图将一个反向图,就可以达到由n + 1到所有点的距离就是我们想要的结果, 也就是add(a,b,c); add(b + n,a + n,c),这样一建立是不是就是两次dij的事情了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值