浅谈最短路计数Dijkstra 与 SPFA实现的不同

版权声明:虽然我很菜,不过转载请标明出处。 https://blog.csdn.net/Patrickpwq/article/details/86602172

由于Dijkstra每个点被“操作”完了后就不会再进行对其他点的松弛操作,而SPFA会入队多次,这就造成了它们在最短路计数实现上的不同

先说Dijkstra的思路,给每个点记一个ans[i]表示源点到当前点最短路的条数

若需要松弛,那么直接ans[y]=ans[x]

否则

Luogu

这个是非常好理解的,正确性也显然——每个点只会对其他点最多松弛1次。

而SPFA不能直接套用,一个显然的例子是

若直接套用会输出3。读者自行手玩一下就会发现,节点2的ans,多次加到了4身上。

那怎么改进呢?一种可行的方式是对于ans数组和now(当前队首),在now进行松弛操作后清零now的ans。

若从当前点now走最短路到对应点vis有三种方式,清零的意义就在于使得每次对vis产生的修改都是1,而不是1,2,2或1,2,3。

另外还需注意在

Luogu

这种情况下y也要入队,否则又会少加。(数据5 5 1 2 1 2 3 1 3 4 1 4 5 1 1 4 3)

两种版本的代码:

#include<bits/stdc++.h>
const int N=2005; 
const int M=4000005;
const int INF=0x3f3f3f3f;
using namespace std;
int n,m;
struct Edge
{
	int to,next,val;
}edge[M];
int first[N],tot;
inline void addedge(int x,int y,int z)
{
	tot++;
	edge[tot].to=y; edge[tot].next=first[x]; edge[tot].val=z; first[x]=tot;
}
int dis[N],ans[N],d[N][N];
typedef pair<int,int> Pair;
priority_queue<Pair,vector<Pair>,greater<Pair> > heap;
bool done[N],inque[N];
void Dijkstra()
{
	memset(done,0,sizeof(done));
	memset(dis,0x3f,sizeof(dis));
	heap.push(make_pair(0,1)); dis[1]=0; ans[1]=1;
	while(!heap.empty())
	{
		int now=heap.top().second;
		heap.pop();
		if(done[now])	continue;
		done[now]=1;
		for(int u=first[now];u;u=edge[u].next)
		{
			int vis=edge[u].to;
			if(dis[now]+edge[u].val==dis[vis])
				ans[vis]+=ans[now];
			else if(dis[now]+edge[u].val<dis[vis])
			{
				dis[vis]=dis[now]+edge[u].val;
				ans[vis]=ans[now];
				heap.push(make_pair(dis[vis],vis));
			}
		}
	}
	if(dis[n]==INF)	cout<<"No answer"<<endl;
	else cout<<dis[n]<<" "<<ans[n]<<endl;
}
queue <int> q;
void SPFA()
{
	memset(dis,0x3f,sizeof(dis));
	q.push(1); dis[1]=0; ans[1]=1;
	while(!q.empty())
	{
		int now=q.front();
		q.pop(); inque[now]=0;
		if(now==n)	continue;
		for(int u=first[now];u;u=edge[u].next)
		{
			int vis=edge[u].to;
			if(dis[now]+edge[u].val==dis[vis])	
				ans[vis]+=ans[now];
			else if(dis[now]+edge[u].val<dis[vis])
			{
				dis[vis]=dis[now]+edge[u].val;
				ans[vis]=ans[now];
			}
			if(ans[vis]&&!inque[vis])
			{
				inque[vis]=1;
				q.push(vis);
			}
		}
		ans[now]=0;
	}
	if(dis[n]==INF)	cout<<"No answer"<<endl;
	else cout<<dis[n]<<" "<<ans[n]<<endl;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	cin>>n>>m;
	memset(d,0x3f,sizeof(d));
	for(int i=1,x,y,z;i<=m;i++)
	{
		cin>>x>>y>>z;
		if(d[x][y]==0||d[x][y]>z)	//有重边 
        {
         	addedge(x,y,z);
			d[x][y]=z;
        }
	}
//	Dijkstra();
	SPFA();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Patrickpwq/article/details/86602172