您的位置:首页 > 其它

[洛谷 1144]最短路计数---spfa+记忆化搜索

2017-08-30 15:49 330 查看

题目描述

给出一个N个顶点M条边的无向无权图,顶点编号为1~N。问从顶点1开始,到其他每个点的最短路有几条。

输入输出格式

输入格式

输入第一行包含2个正整数N,M,为图的顶点数与边数。

接下来M行,每行两个正整数x, y,表示有一条顶点x连向顶点y的边,请注意可能有自环与重边。

输出格式

输出包括N行,每行一个非负整数,第i行输出从顶点1到顶点i有多少条不同的最短路,由于答案有可能会很大,你只需要输出mod 100003后的结果即可。如果无法到达顶点i则输出0。

输入输出样例

输入样例#1:

5 7

1 2

1 3

2 4

3 4

2 3

4 5

4 5

输出样例#1:

1

1

1

2

4

说明

1到5的最短路有4条,分别为2条1-2-4-5和2条1-3-4-5(由于4-5的边有2条)。

对于20%的数据,N ≤ 100;

对于60%的数据,N ≤ 1000;

对于100%的数据,N<=1000000,M<=2000000。

分析

首先不用说,先求出最短路(默认边权为1)。至于求路径的个数,可以用动规的思路去分析:令d[j]为1至j点的最短路径数,那么,满足d[i]+=d[j]{ i为j可以到达的点,且j在1至i的最短路径上 }

于是,有如下公式 d[i]=sum( d[j] ){ j在1至i的最短路径上 }

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout);

using namespace std;

struct Edge //边表(链式向前星)
{
int to;
int next;
int w;
};

int mo=100003;
int cnt;
int n,m;
int head[1000010];
Edge edge[4000010];
int dis[1000010];
bool vis[1000010];
int ans[1000010];

void add(int x,int y,int z)
{
++cnt;
edge[cnt].to=y;
edge[cnt].w=z;
edge[cnt].next=head[x];
head[x]=cnt;
}

void spfa()
{
queue<int>q;
q.push(1);
vis[1]=1;
for(int t=1;t;)
{
int p=q.front(); q.pop(); --t;
vis[p]=0;
for(int i=head[p];i;i=edge[i].next)
if(dis[edge[i].to]>dis[p]+edge[i].w)
{
int y=edge[i].to;
dis[y]=dis[p]+edge[i].w;
if(!vis[y])
{
vis[y]=1;
++t;
q.push(y);
}
}
}
}

int get_ans(int p)
{
if(vis[p]) return ans[p];
int sum=0;
for(int i=head[p];i;i=edge[i].next)
if(dis[edge[i].to]+edge[i].w==dis[p])
sum=(sum+get_ans(edge[i].to))%mo;
vis[p]=1;
return ans[p]=sum;
}

int main()
{
open("1144");

scanf("%d %d",&n,&m);
for(int i=1;i<=m;++i)
{
int x, y;
scanf(" %d %d",&x,&y);
add(x,y,1);
add(y,x,1);
}
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
spfa();
memset(vis,0,sizeof(vis));
vis[1]=1; //是否求出ans
ans[1]=1; //初始化
for(int i=1;i<=n;++i)
printf("%d\n",get_ans(i)); // 记忆化搜索

close;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: