pku3463求最短路和路径长度比其小一的总方法数

         求次短路什么的最好用Dijkstra,因为每次取最小的,有序使得最后结果。

        这题相当纠结。首先的想法是先把起点到其它点的最短距离与最短路径数目算出来,自然就知道S到T最短路的条数。然后枚举每条边(u,v,w),如果S到u的最短路距离+w+v到T的最短距离等于S到T的最短距离+1时,那么肯定满足题意,增加的条数为S到u最短路的路线数*v到T最短路的路线数。但这样会有问题,一条比最短路径长度大一的路径会算很多遍。所以我后面加了一个条件,就是v在最短路的路径上,即S到v与v到T的最短距离和等于S到T的最短距离。这样的话,每条比最短路长一的路径有且仅会算一次。

         用SPFA求前面最短路的条数后,一直WA,开始以为是算条数的时候错了。但细想之下,觉得不可能错,后面还是cyx用Dijkstra过了之后,我再用那方法A了之后,然后忽然想到说不定SPFA错的,所以就把SPFA换成Dijkstra就过了。。。细想之后,发现确实有问题。。。解释如注释。。。

#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<string.h>
using namespace std;

const int maxn=1100;
const int maxm=11000;
const int maxint=0x3fffffff;
struct point
{
	int u,v,w;
}p0,p[maxm];
vector<point> e[maxn],e1[maxn];
int n,dist[maxn],dist1[maxn],num[maxn],num1[maxn];

/*void SPFA(int s,int dist[],vector<point> e[],int num[])
{
	int i,k,q[maxn],head=0,tail=0;
	bool inq[maxn];
	for(i=1;i<=n;i++)
		dist[i]=maxint,inq[i]=false,num[i]=0;
	dist[s]=0;
	q[tail++]=s;
	num[s]=1;
	inq[s]=true;
	while(head!=tail)
	{
		i=q[head];
		inq[i]=false;
		head=(head+1)%maxn;
		for(k=0;k<e[i].size();k++)
		{
			if(dist[e[i][k].v]>dist[i]+e[i][k].w)
			{
				num[e[i][k].v]=num[i];
				dist[e[i][k].v]=dist[i]+e[i][k].w;
				if(!inq[e[i][k].v])
				{
					inq[e[i][k].v]=true;
					q[tail]=e[i][k].v;
					tail=(tail+1)%maxn;
				}
			}//这里如果相等,那么后面的路径应该要更新,所以应该要入队,但入队的话后面又会多计算,所以不能简单的处理。应该要用双关键字
			else if(dist[e[i][k].v]==dist[i]+e[i][k].w)
				num[e[i][k].v]+=num[i];
		}
	}
}*/
void D(int s,int dist[],vector<point> e[],int num[])
{
	int ii,i,j,k;
	bool visit[maxn];
	for(i=1;i<=n;i++)
		dist[i]=maxint,num[i]=0,visit[i]=false;
	dist[s]=0;
	num[s]=1;
	for(i=1;i<=n;i++)
	{
		for(k=maxint,ii=-1,j=1;j<=n;j++)
			if(!visit[j]&&dist[j]<k)
			{
				k=dist[j];
				ii=j;
			}
		if(ii==-1) break;
		visit[ii]=true;
		for(j=0;j<e[ii].size();j++)
		{
			if(visit[e[ii][j].v]) continue;
			if(dist[e[ii][j].v]>dist[ii]+e[ii][j].w)
			{
				dist[e[ii][j].v]=dist[ii]+e[ii][j].w;
				num[e[ii][j].v]=num[ii];
			}
			else if(dist[e[ii][j].v]==dist[ii]+e[ii][j].w)
				num[e[ii][j].v]+=num[ii];
		}
	}
}

int main()
{
//	freopen("1.in","r",stdin);
//	freopen("1.out","w+",stdout);
	int t,m,S,T,i,sum;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)
			e[i].clear(),e1[i].clear();
		for(i=1;i<=m;i++)
		{
			scanf("%d%d%d",&p0.u,&p0.v,&p0.w);
			p[i]=p0;
			e[p0.u].push_back(p0);
			swap(p0.u,p0.v);
			e1[p0.u].push_back(p0);
		}
		scanf("%d%d",&S,&T);
	//	SPFA(S,dist,e,num);
		//SPFA(T,dist1,e1,num1);
		D(S,dist,e,num);
		D(T,dist1,e1,num1);
	/*	for(i=1;i<=n;i++)
			printf("%d ",dist[i]);
		printf("\n");
		for(i=1;i<=n;i++)
			printf("%d ",dist1[i]);
		printf("\n");
		for(i=1;i<=n;i++)
			printf("%d %d\n",num[i],num1[i]);*/
		sum=num[T];
		for(i=1;i<=m;i++)
			if(dist[p[i].u]+dist1[p[i].v]+p[i].w==dist[T]+1&&dist[p[i].v]+dist1[p[i].v]==dist[T])
			{
				sum+=num[p[i].u]*num1[p[i].v];
			//	printf("%d %d %d..\n",p[i].u,p[i].v,p[i].w);
			}
		printf("%d\n",sum);
	}
	return 0;
}

      用Dijkstra求时,将点一分为二,一个点分成最短的与次短的两个状态,然后每次找最小的状态,不断更新。

#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<string.h>
using namespace std;

const int maxn=1100;
const int maxm=11000;
const int maxint=0x3fffffff;
struct point
{
	int u,v,w;
}p0,p[maxm];
vector<point> e[maxn];
int n,S,T,dist[maxn],dist1[maxn],num[maxn],num1[maxn];

void Dijkstra()
{
	int i,j,k,ii;
	bool v1[maxn],v2[maxn],tag;
	for(i=1;i<=n;i++)
		v1[i]=false,v2[i]=false,dist[i]=maxint,dist1[i]=maxint,num[i]=0,num1[i]=0;
	dist[S]=0;
	num[S]=1;

	for(i=1;i<=2*n;i++)
	{
		for(k=maxint,ii=-1,j=1;j<=n;j++)
		{
			if(!v1[j]&&dist[j]<k)
			{
				tag=false;
				ii=j;
				k=dist[j];
			}
			if(!v2[j]&&dist1[j]<k)
			{
				tag=true;
				ii=j;
				k=dist1[j];
			}
		}
		if(ii==-1) break;
		if(!tag)//最小的状态为最短路
		{
			v1[ii]=true;
			for(j=0;j<e[ii].size();j++)
			{
				if(dist[e[ii][j].v]>dist[ii]+e[ii][j].w)
				{
					dist1[e[ii][j].v]=dist[e[ii][j].v];
					num1[e[ii][j].v]=num[e[ii][j].v];
					dist[e[ii][j].v]=dist[ii]+e[ii][j].w;
					num[e[ii][j].v]=num[ii];
				}
				else if(dist[e[ii][j].v]==dist[ii]+e[ii][j].w)
					num[e[ii][j].v]+=num[ii];
				else if(dist1[e[ii][j].v]>dist[ii]+e[ii][j].w)
				{
					dist1[e[ii][j].v]=dist[ii]+e[ii][j].w;
					num1[e[ii][j].v]=num[ii];
				}
				else if(dist1[e[ii][j].v]==dist[ii]+e[ii][j].w)
					num1[e[ii][j].v]+=num[ii];
			}
		}
		else//最小的状态为次短路
		{
			v2[ii]=true;
			for(j=0;j<e[ii].size();j++)
			{
				if(dist1[e[ii][j].v]>dist1[ii]+e[ii][j].w)
				{
					dist1[e[ii][j].v]=dist1[ii]+e[ii][j].w;
					num1[e[ii][j].v]=num1[ii];
				}
				else if(dist1[e[ii][j].v]==dist1[ii]+e[ii][j].w)
					num1[e[ii][j].v]+=num1[ii];
			}
		}
	}

	if(dist[T]+1==dist1[T]) printf("%d\n",num1[T]+num[T]);
	else printf("%d\n",num[T]);
}
int main()
{
	int t,m,i;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)
			e[i].clear();
		for(i=1;i<=m;i++)
		{
			scanf("%d%d%d",&p0.u,&p0.v,&p0.w);
			e[p0.u].push_back(p0);
		}
		scanf("%d%d",&S,&T);
		Dijkstra();
	}
	return 0;
}



 

你可能感兴趣的:(pku3463求最短路和路径长度比其小一的总方法数)