【poj3463】Sightseeing(次短路-----每条边经过不止一次计数)

题目:我是超链接

题意:走出最短路的方法数,如果次短路只比最短路小1,那也是可取的。输出总的方法数

题解:

我们发现只要路径中有一条边不一样就全都不一样,那么是每条边经过不止一次的次短路?

本来考虑用A*的k短路算法,但是发现会TLE||MLE,因为发现此题的答案会超过10^8,如此之大的数量级,优先队列就把内存撑破了,而POJ2449中则有限制k<=1000,所以不会MLE。

那还是老老实实用dij吧,dij虽然也要用到优先队列,但是我们可以省略ta(比如手动小根堆什么的),注意这里我们循环了2*n-1次,为什么我们要循环2*n-1次?显然这道题中我们每一条边都需要考虑,这不是在求最短的一条,说白了是让你求出所有的可能组合,姑且就理解为其中n-1次是用来求最短路的,还有n次是次短路的

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#define INF 1e9
#define N 1005
#define M 10005
using namespace std;
int cnt[N][2],tot,nxt[M],point[N],v[M],c[M],dis[N][2],n;
bool vis[N][2];
void addline(int x,int y,int z){++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;}
void dij(int s,int t)
{
	int i,j,k,fl;
	memset(dis,0x7f,sizeof(dis)); memset(cnt,0,sizeof(cnt));
	memset(vis,0,sizeof(vis));
	dis[s][0]=0;
    cnt[s][0]=1;
    for (i=1;i<=2*n-1;i++)
    {
    	int minn=0x7fffffff;k=0;
    	for (j=1;j<=n;j++)
    	  if (!vis[j][0] && dis[j][0]<minn)
    	  {
    	  	minn=dis[j][0]; k=j; fl=0;
		  }
		  else if (!vis[j][1] && dis[j][1]<minn)
		  {
		  	minn=dis[j][1]; k=j; fl=1;
		  }
		vis[k][fl]=1;
		for (j=point[k];j;j=nxt[j])
		{
			if (dis[v[j]][0]>c[j]+minn)
			{
				dis[v[j]][1]=dis[v[j]][0];
				dis[v[j]][0]=c[j]+minn;
				cnt[v[j]][1]=cnt[v[j]][0];
				cnt[v[j]][0]=cnt[k][fl];
			}
			else if (dis[v[j]][0]==c[j]+minn)
			{
				cnt[v[j]][0]+=cnt[k][fl];
			}
			else if (dis[v[j]][1]>c[j]+minn)
			{
				dis[v[j]][1]=c[j]+minn;
				cnt[v[j]][1]=cnt[k][fl];
			}
            else if (dis[v[j]][1]==c[j]+minn)
            {
            	cnt[v[j]][1]+=cnt[k][fl];
			}
		}
	}
	if (dis[t][0]+1==dis[t][1]) printf("%d\n",cnt[t][0]+cnt[t][1]);
	else printf("%d\n",cnt[t][0]);
}
int main()
{
	int T,m,i,s,t;
	scanf("%d",&T);
	while (T--)
	{
		tot=0; memset(point,0,sizeof(point));
		scanf("%d%d",&n,&m);
		for (i=1;i<=m;i++)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			addline(x,y,z);
		}
		scanf("%d%d",&s,&t);
		dij(s,t);
	}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值