洛谷P1144 最短路计数 BFS、堆优化的Dijkstra、SPFA。三种解法详解。

题源:https://www.luogu.org/problem/P1144
思路来源:https://www.luogu.org/problemnew/solution/P1144

题意:计数从编号为1的结点到其他节点最短路的数量。

解法1:BFS

这个解法不太好想 因为路径长度都是1,可以用BFS来解
0、这个题稍微简单一点的地方就是边权均为1,若边权不是1的话,这题就不能用。
1、广搜,一个点只入队一次。
2、核心思想:通过当前点去确定下一个点的深度(路径长度):具体描述是,看当前点指向的所有点有没有确定过,若没确定过(book[i]==0),那入队并确定深度,此时因为第一次入队,路径条数就等于上一个点的最短路条数。若确定过,说明不是第一次入队,就判断当前点是不是比下一个点浅一层,然后再加上当前点的最短路数量即可。
为什么这么做?首先因为保证了“下一个点”是“当前点”可达的,所以每当当前点去扩下一个点的时候,都已经保证了是可达的,其次再保证当前点是下一个点的上一层点即可

核心代码:

			if(book[v]==0){//若该点没来过  book是防止一个点多次入队 
				dis[v]=dis[u]+1;//取先入队的!!! 
				book[v]=1;
				q.push(v);
			}
			if(dis[v]==dis[u]+1){//防止同级的影响 
				cnt[v]=(cnt[v]+cnt[u])%mod;//用父点来更新当前点
			}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define mod 100003
using namespace std;
const int maxn=1e6+10;
int n,m,num;
struct Side{
	int v,next;
}side[maxn];
int head[maxn],dis[maxn],cnt[maxn],book[maxn];
queue<int> q;
void add(int u,int v){
	num++;
	side[num].v=v;
	side[num].next=head[u];
	head[u]=num;
}
void init(){
	num=0;
	cl(head,-1);
	cl(cnt,0);
	cl(dis,inf);
	dis[1]=0;
}
int main(){
	init();
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	q.push(1);
	book[1]=1;
	cnt[1]=1;//初始化为1 
	while(!q.empty()){
		int u=q.front();
		q.pop();
		//book[u]=0;不行  广搜!  这里只入队一次!!! 
		for(int i=head[u];i!=-1;i=side[i].next){
			int v=side[i].v;
			if(book[v]==0){//若该点没来过  book是防止一个点多次入队 
				dis[v]=dis[u]+1;//取先入队的!!! 
				book[v]=1;
				q.push(v);
			}
			if(dis[v]==dis[u]+1){//防止同级的影响 
				cnt[v]=(cnt[v]+cnt[u])%mod;//用父点来更新当前点
			}
		}
	}
	for(int i=1;i<=n;i++){
		printf("%d\n",cnt[i]);
	}
	return 0;
}

解法2:堆优化的Dijkstra

这个解法2的核心代码和下面的解法3一样。其他题解讲的很清楚了,就是:出现等长的最短路就加上,出现更小的就赋值。

			if(dis[v]>dis[u]+1){
				dis[v]=dis[u]+1;
				cnt[v]=cnt[u];
					temp.id=v;
					temp.dis=dis[v];
					q.push(temp);
			}else if(dis[v]==dis[u]+1){
				cnt[v]+=cnt[u];
				cnt[v]%=mod;//刚开始写错了TAT 
			}

AC代码如下:

#include<iostream>//dijkstra堆优化
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define mod 100003
using namespace std;
const int maxn=1e6+10;
int n,m,num;
struct Side{
	int v,next;
}side[maxn];
int head[maxn],dis[maxn],cnt[maxn],book[maxn];
struct node{
	int id,dis;
	
}; bool operator<(node x,node y){
		return x.dis>y.dis;
	}
priority_queue<node> q;
void add(int u,int v){
	num++;
	side[num].v=v;
	side[num].next=head[u];
	head[u]=num;
}
void init(){
	num=0;
	cl(head,-1);
	cl(cnt,0);
	cl(dis,inf);
	dis[1]=0;
}
int main(){
	init();
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	node temp;
	temp.id=1;
	temp.dis=0;
	q.push(temp);
	cnt[1]=1;//初始化为1 
	while(!q.empty()){
		int u=q.top().id;
		q.pop();
		if(book[u]) continue;
		book[u]=1;
		for(int i=head[u];i!=-1;i=side[i].next){
			int v=side[i].v;
			if(dis[v]>dis[u]+1){
				dis[v]=dis[u]+1;
				cnt[v]=cnt[u];
				//if(!book[v]){//这里不需要这个  尽管入列 用的时候只用最小的 
					temp.id=v;
					temp.dis=dis[v];
					q.push(temp);
					//book[v]=1;这里不加入队列
				//}
			}else if(dis[v]==dis[u]+1){
				cnt[v]+=cnt[u];
				cnt[v]%=mod;//刚开始写错了TAT 
			}
		}
	}
	for(int i=1;i<=n;i++){
		printf("%d\n",cnt[i]);
	}
	return 0;
}

解法3:SPFA

AC代码如下 其实不要说为什么之际上代码 其实解题方法就是当前算法多一个cnt数组记录最小条数即可

#include<iostream>//spfa算法!!! 
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define mod 100003
using namespace std;
const int maxn=1e6+10;
int n,m,num;
struct Side{
	int v,next;
}side[maxn];
int head[maxn],dis[maxn],cnt[maxn],book[maxn];
queue<int> q;
void add(int u,int v){
	num++;
	side[num].v=v;
	side[num].next=head[u];
	head[u]=num;
}
void init(){
	num=0;
	cl(head,-1);
	cl(cnt,0);
	cl(dis,inf);
	dis[1]=0;
}
int main(){
	init();
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	q.push(1);
	book[1]=1;
	cnt[1]=1;//初始化为1 
	while(!q.empty()){
		int u=q.front();
		q.pop();
		book[u]=0;
		for(int i=head[u];i!=-1;i=side[i].next){
			int v=side[i].v;
			if(dis[v]>dis[u]+1){
				dis[v]=dis[u]+1;
				cnt[v]=cnt[u];
				if(book[v]==0){
					q.push(v);
				}
			}else if(dis[v]==dis[u]+1){
				cnt[v]+=cnt[u];
				cnt[v]%=mod;
			}
		}
	}
	for(int i=1;i<=n;i++){
		printf("%d\n",cnt[i]);
	}
	return 0;
}

题目描述
给出一个
NN
N个顶点
MM
M条边的无向无权图,顶点编号为
1−N1-N
1−N。问从顶点
11
1开始,到其他每个点的最短路有几条。
输入格式
第一行包含
22
2个正整数
N,MN,M
N,M,为图的顶点数与边数。
接下来
MM
M行,每行
22
2个正整数
x,yx,y
x,y,表示有一条顶点
xx
x连向顶点
yy
y的边,请注意可能有自环与重边。
输出格式

NN
N行,每行一个非负整数,第
ii
i行输出从顶点
11
1到顶点
ii
i有多少条不同的最短路,由于答案有可能会很大,你只需要输出
ansmod100003 ans \bmod 100003
ansmod100003后的结果即可。如果无法到达顶点
ii
i则输出
00
0。
输入输出样例
输入 #1
复制
5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5
输出 #1
复制
1
1
1
2
4
说明/提示
11
1到
55
5的最短路有
44
4条,分别为
22
2条
1−2−4−51-2-4-5
1−2−4−5和
22
2条
1−3−4−51-3-4-5
1−3−4−5(由于
4−54-5
4−5的边有
22
2条)。
对于
20%20%
20%的数据,
N≤100N ≤ 100
N≤100;
对于
60%60%
60%的数据,
N≤1000N ≤ 1000
N≤1000;
对于
100%100%
100%的数据,
N<=1000000,M<=2000000N<=1000000,M<=2000000
N<=1000000,M<=2000000。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值