题解——跑路(倍增优化DP+最短路)

题目链接

P1613 跑路

题目描述

小 A 的工作不仅繁琐,更有苛刻的规定,要求小 A 每天早上在 \(6:00\) 之前到达公司,否则这个月工资清零。可是小 A 偏偏又有赖床的坏毛病。于是为了保住自己的工资,小 A 买了一个空间跑路器,每秒钟可以跑 \(2^k\) 千米(\(k\) 是任意自然数)。当然,这个机器是用 longint 存的,所以总跑路长度不能超过 maxlongint 千米。小 A 的家到公司的路可以看做一个有向图,小 A 家为点 \(1\),公司为点 \(n\),每条边长度均为一千米。小 A 想每天能醒地尽量晚,所以让你帮他算算,他最少需要几秒才能到公司。数据保证 \(1\)\(n\) 至少有一条路径。

输入格式

第一行两个整数 \(n,m\),表示点的个数和边的个数。

接下来 \(m\) 行每行两个数字 \(u,v\),表示一条 \(u\)\(v\) 的边。

输出格式

一行一个数字,表示到公司的最少秒数。

输入输出样例 #1

输入 #1

4 4
1 1
1 2
2 3
3 4

输出 #1

1

说明/提示

【样例解释】

\(1 \to 1 \to 2 \to 3 \to 4\),总路径长度为 \(4\) 千米,直接使用一次跑路器即可。

【数据范围】

\(50\%\) 的数据满足最优解路径长度 \(\leq 1000\)

\(100\%\) 的数据满足 \(2\leq n \leq 50\)\(m \leq 10 ^ 4\),最优解路径长度 \(\leq\) maxlongint

思路+AC代码

这题的做法看似理所当然,实则我根本没想到

首先我们肯定能看出来这里的最短时间并不是最短路,比如路径长分别为3和4,3的需要2秒,而4只需要1秒。

再一看2k这不是倍增吗?所以我们可以把图改变一下,距离为2k的两点直接连一条权值为1的边

但是其他边怎么办呢?这就需要状态转移了,也就是DP.

我们设f[u][v][k]表示从v到u走2k能不能到,能的话是1,不能是0.

状态转移:if(f[i][k][ljy-1]= =1 && f[k][j][ljy-1]==1)f[i][j][ljy]=1.

应该蛮好理解的,k是一个中间点,如果i,k以及k,j可以经过2ljy-1到达,那么从i到j就是2*(2ljy-1)=2ljy.同时也就意味着,i,j是可以直接到达的,边权为1.

接着就可以使用我们的老朋友Floyed,代码很好懂.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=60,M=10010;
int n,m;
int f[N][N][65],dis[N][N];
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}inline int max(int x,int y){return (x<y)?y:x;}inline int min(int x,int y){return (x<y)?x:y;}
signed main()
{
	n=read();m=read();
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=m;i++)
	{
		int u,v;u=read();v=read();
		f[u][v][0]=1;
		dis[u][v]=1;
	}
	for(int ljy=1;ljy<=64;ljy++)
		for(int k=1;k<=n;k++)
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
				{
					if(f[i][k][ljy-1]==1&&f[k][j][ljy-1]==1)
					{
						f[i][j][ljy]=1;dis[i][j]=1;
					}
				}
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	write(dis[1][n]);
	return 0;
}

反思:这道题突破点就在于怎么转化边权,其实设问也能给我们一些启示,最后问的是时间而不是路径长,所以边权是要转化成时间的.DP是倍增DP的典型做法,多做题熟悉熟悉就好,至于floyed,如果前面都想出来了应该不难想.

欢迎指出问题或错误,点个推荐再走啦

posted @ 2025-04-05 21:49  Crab2016  阅读(22)  评论(0)    收藏  举报