《算法竞赛进阶指南》0x6B T1 Sightseeing

题目传送门

题目描述

“您的个人假期”旅行社组织了一次比荷卢经济联盟的巴士之旅。

比荷卢经济联盟有很多公交线路。

每天公共汽车都会从一座城市开往另一座城市。

沿途汽车可能会在一些城市(零或更多)停靠。

旅行社计划旅途从 S 城市出发,到 F 城市结束。

由于不同旅客的景点偏好不同,所以为了迎合更多旅客,旅行社将为客户提供多种不同线路。

游客可以选择的行进路线有所限制,要么满足所选路线总路程为 S 到 F 的最小路程,要么满足所选路线总路程仅比最小路程多一个单位长度。

在这里插入图片描述

如上图所示,如果 S=1,F=5,则这里有两条最短路线 1→2→5,1→3→5,长度为 6;有一条比最短路程多一个单位长度的路线 1→3→4→5,长度为 7。

现在给定比荷卢经济联盟的公交路线图以及两个城市 S 和 F,请你求出旅行社最多可以为旅客提供多少种不同的满足限制条件的线路。

题解

这是一道裸的最短路,需要在最短路的模板上加一些小小的操作
求最短路的同时去记录次短路,并额外开一个数组记录最短路和次短路的总条数,这些操作在跑最短路的过程中都是很好进行的
具体讲一下转移过程
1.当这条路径比最短路小,将最短路的条数和长度转移给次短路,并更新最短路的条数和长度
2.当这条路径和最短路一样长,那就将最短路的条数增加
3.当这条路径比次短路小,更新次短路的条数和长度
4.当这条路径和次短路一样长,那就将次短路的条数增加
注意,上述条件均需满足if else 关系,如果没有else,那么就会将最短路和次短路混为一谈
我们需要求得的是最短路的条数和比最短路距离仅大1的路径条数
如果存在比最短路距离仅大1的路径,那么它一定会作为次短路
我们只需要判断一下次短路是不是这样的一条仅比最短路距离大1 的路径
如果是就加上次短路的路径条数即可

code
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
const int M=100100;
int n,m,s,t;
struct Star
{
	int nxt,val,to;
}edge[M];
int head[N],tot=0;
void Lian(int x,int y,int z)
{
	tot++;
	edge[tot].nxt=head[x];
	edge[tot].to=y;
	edge[tot].val=z;
	head[x]=tot;
}
struct Point
{
	int dd,p,type;
	bool operator<(const Point &x) const{
		return dd>x.dd;
	}
};
int dis[N][2],vis[N][2],cnt[N][2];
void dijkstra()
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	Point a;
	a.dd=0;a.p=s;a.type=0;
	dis[s][0]=0;
	cnt[s][0]=1;
	priority_queue <Point> q;
	q.push(a);
	while(!q.empty())
	{
		a=q.top();
		q.pop();
		int x=a.p,f=a.type;
		if(vis[x][f]) continue;
		vis[x][f]=1;
		for(int i=head[x];i;i=edge[i].nxt)
		{
			int y=edge[i].to;
			if(dis[y][0]>dis[x][f]+edge[i].val)
			{
				dis[y][1]=dis[y][0];
				cnt[y][1]=cnt[y][0];
				a.dd=dis[y][1],a.p=y,a.type=1;
				q.push(a);
				dis[y][0]=dis[x][f]+edge[i].val;
				cnt[y][0]=cnt[x][f];
				a.dd=dis[y][0],a.p=y,a.type=0;
				q.push(a);
			}
			else if(dis[y][0]==dis[x][f]+edge[i].val) cnt[y][0]+=cnt[x][f];
			else if(dis[y][1]>dis[x][f]+edge[i].val)
			{
				dis[y][1]=dis[x][f]+edge[i].val;
				cnt[y][1]=cnt[x][f];
				a.dd=dis[y][1],a.p=y,a.type=1;
				q.push(a);
			}
			else if(dis[y][1]==dis[x][f]+edge[i].val) cnt[y][1]+=cnt[x][f]; 
		}
	}
 } 
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		memset(head,0,sizeof(head));
		for(int i=1;i<=m;i++)
		{
			int a,b,l;
			cin>>a>>b>>l;
			Lian(a,b,l);
		}
		cin>>s>>t;
		dijkstra();
		int ans=cnt[t][0];
		if(dis[t][1]-1==dis[t][0]) ans+=cnt[t][1];
		cout<<ans<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值