洛谷 P1613 跑路 倍增+最短路

题目传送门

大致题意:

给定一张\(n\)个结点\(m\)条边的有向图,边权固定为1,每秒可以移动\(2^t\)\(t\)任意值)。求从\(1\)\(n\)的最短所需时间。

其中\(n\leq 50,m\leq 10000,dis_{1-n}\leq int_{max}\)


第一眼是个最短路,然后发现不对劲。

因为从\(1\)\(n\)的最短路不一定是所求答案,即二进制下\(1\)的个数不一定最少。

举个栗子:

在这张图中,很明显最短路是\(1-2-4-7\),但需要\(2+1\)\(2\)秒。而另一条路虽然长度为\(4\),但只需要一次\(2^2\)就可以到达,时间只需要\(1\)秒。

于是我们需要根据题目中\(2^t\)这个提示,考虑如何融入倍增的思想。

最终的算法肯定还是落脚在最短路上,但是这个边不应该是最初输入进来的,而是通过我们的计算,可以一步到达的两个点之间连边。

什么情况下两个点可以一步到达?

对于\((u,v)\)是否能通过一个\(2^t\)跨过去,我们如果能找到一个\(k\)满足\((u,k)\)能够\(2^{t-1}\)跨过去,并且,\((k,v)\)

也可以\(2^{t-1}\)跨过去,那就一定可行。

于是考虑用\(dp[u][v][t]\)表示,从\(u\)\(v\)是否能移动\(2^t\)到达。

对于输入的边\((u,v)\),当然是可以通过\(2^0\)距离到达的,那么\(dp[u][v][0]=1\)。并连上\(u\)\(v\)

此外就如上所述,遍历其他点\(k\),如果有dp[u][k][t-1]&dp[k][v][t-1]==1,那么dp[u][v][t]=1,并且把\(u\),\(v\)连上。

完成了建边之后,直接跑最短路即可。考虑到\(n\)的范围只有\(50\),于是方便地选用\(Floyd\)

另外,\(t\)只需要枚举到\(31\)即可(\(int_{max}=2^{31}-1\))。

code:

#include<bits/stdc++.h>
using namespace std;
const int N=70;
int dis[N][N],dp[N][N][N];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	memset(dis,0x3f,sizeof dis);
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		dis[u][v]=1;
		dp[u][v][0]=1;
	}
	for(int t=1;t<32;t++)
		for(int k=1;k<=n;k++)
			for(int u=1;u<=n;u++)
				for(int v=1;v<=n;v++)
					if(dp[u][k][t-1]&dp[k][v][t-1])
						dis[u][v]=dp[u][v][t]=1;
	for(int k=1;k<=n;k++)
		for(int u=1;u<=n;u++)
			for(int v=1;v<=n;v++)
				dis[u][v]=min(dis[u][v],dis[u][k]+dis[k][v]);
	printf("%d\n",dis[1][n]);
	return 0;
}
posted @ 2020-09-29 21:37  摸鱼酱  阅读(126)  评论(0编辑  收藏  举报