有关最短路计数的问题

想必大家都会求最短路吧,这里就不再多说了;

我看有很多人在一顿套模板,什么dijkstra,SPFA等等,这是可以的;

但身为OIer,思路要开阔对不对?

首先我们注意到,我们可以利用bfs来求每个点的深度。因为在所有边边权为1的时候,点的深度就是点的最短距离;

这样在写法上便少了队列优化SPFA中退栈时还要把标记抹除这一操作,会大大提高算法速度;

在bfs的时候,我们不仅仅要从一个点的父亲继承最短路,还要继承方案数;

有个细节,起点的方案数要记为1;

#include 
#define int long long
#define p 100003
using namespace std;
struct littlestar{
	int to;
	int nxt;
}star[4000010];
int head[4000010],cnt;
inline void add(register int u,register int v)
{
	star[++cnt].to=v;
	star[cnt].nxt=head[u];
	head[u]=cnt;
}
int n,m;
long long g[1000010];
int dis[1000010],vis[1000010];
queue q;
int st,ed;
inline void bfs(register int s)
{
	memset(g,0,sizeof(g));
	memset(dis,0x3f,sizeof(dis));
	q.push(s);
	dis[s]=0;
	vis[s]=1;
	g[s]=1;
	while(q.size()){
		register int u=q.front();
		q.pop();
		for(register int i=head[u];i;i=star[i].nxt){
			register int v=star[i].to;
			if(dis[v]==dis[u]+1) g[v]=(g[u]+g[v])%p;
			if(vis[v]) continue;
			vis[v]=1;
			dis[v]=dis[u]+1;
			g[v]=g[u]%p;
			q.push(v);
		}
	}
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(register int i=1;i<=m;i++){
		register int u,v;
		scanf("%lld%lld",&u,&v);
		add(u,v);
		add(v,u);
	}
	bfs(1);
	for(int i=1;i<=n;i++){
		printf("%lld\n",g[i]);
	}
}

 

有闲时间的读者可以看一下这道题的扩展:

一张N*M的无向图,有k个特殊的点;你从1号点出发,必须停留且只停留在一个特殊点休息一段时间,然后到达n号点;(可以一次经过多个特殊点,但如果在不同的特殊点停留休息,就算不同种方案);求符合条件的**最短路**的**方案数**;

其实这道题稍加思考就可以得到正解:分别从1号点和n号点bfs()求出每个点到1号点和n号点的最短路及最短路下的方案数;我们根据乘法原理把最短路下的方案数乘起来就可以了;

#include 
#define int long long
#define p 1000000007
using namespace std;
struct littlestar{
	int to;
	int nxt;
}star[8000010];
int head[8000010],cnt;
void add(int u,int v)
{
	star[++cnt].to=v;
	star[cnt].nxt=head[u];
	head[u]=cnt;
}
int n,m;
char s[1010][1010];
int query[1010][1010];
long long g[1000010];
int water[1000010];
int dis[1000010],vis[1000010];
queue q;
int st,ed;
void bfs(int s)
{
	memset(g,0,sizeof(g));
	memset(dis,0x3f,sizeof(dis));
	q.push(s);
	dis[s]=0;
	vis[s]=1;
	g[s]=1;
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=star[i].nxt){
			int v=star[i].to;
			if(dis[v]==dis[u]+1) g[v]=(g[u]+g[v])%p;
			if(vis[v]) continue;
			vis[v]=1;
			dis[v]=dis[u]+1;
			g[v]=g[u]%p;
			q.push(v);
		}
	}
}
int stdis[1000010];
long long stg[1000010];
signed main()
{
	scanf("%lld%lld",&n,&m);
	int num=0;
	for(register int i=1;i<=n;i++){
		for(register int j=1;j<=m;j++){
			query[i][j]=++num;
		}
	}
	for(register int i=1;i<=n;i++){
		scanf("%s",s[i]+1);
	}	
	for(register int i=1;i<=n;i++){
		for(register int j=1;j<=m;j++){
			if(s[i][j]=='@') st=query[i][j];
			if(s[i][j]=='#') ed=query[i][j];
			if(s[i][j]=='*') water[++water[0]]=query[i][j];
			if(s[i][j]=='X') continue;
			if(i-1>0&&s[i-1][j]!='X'){
				add(query[i][j],query[i-1][j]);
			}
			if(j-1>0&&s[i][j-1]!='X'){
				add(query[i][j],query[i][j-1]);
			}
			if(i+1<=n&&s[i+1][j]!='X'){
				add(query[i][j],query[i+1][j]);
			}
			if(j+1<=m&&s[i][j+1]!='X'){
				add(query[i][j],query[i][j+1]);
			}
		}
	}
	bfs(st);
	for(int i=1;i<=num;i++) stdis[i]=dis[i],stg[i]=g[i];
	memset(vis,0,sizeof(vis));
	bfs(ed);
	int ans=INT_MAX;
	for(int i=1;i<=water[0];i++){
		ans=min(ans,stdis[water[i]]+dis[water[i]]);
	}
	long long fians=0;
	for(int i=1;i<=water[0];i++){
		if(stdis[water[i]]+dis[water[i]]==ans){
			fians=(fians+stg[water[i]]*g[water[i]]%p)%p;
		}
	}
	if(ans==INT_MAX){
		cout<<"-1 0";
		return 0;
	}
	cout< 
 

 

你可能感兴趣的:(有关最短路计数的问题)