题源: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。