zoukankan      html  css  js  c++  java
  • 洛谷 P1613 跑路 倍增+最短路

    题目传送门

    大致题意:

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

    其中(nleq 50,mleq 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
    ",dis[1][n]);
    	return 0;
    }
  • 相关阅读:
    SQL Server 索引的自动维护 <第十三篇>
    SQL Server 索引的图形界面操作 <第十二篇>
    python处理时间戳
    今天又犯了Java/Scala里面substring的错误
    新浪系统工程师笔试--shell
    把DEDE的在线文本编辑器换成Kindeditor不显示问题
    C语言 EOF是什么?
    Windows Server 2012 R2超级虚拟化之七 远程桌面服务的增强
    C++数据结构之最小生成树
    python sqlite 查询表的字段名 列名
  • 原文地址:https://www.cnblogs.com/moyujiang/p/13752061.html
Copyright © 2011-2022 走看看