[dp] 飞翔

题目描述

鹰最骄傲的就是翱翔,但是鹰们互相都很嫉妒别的鹰比自己飞的快,更嫉妒其他的鹰比自己飞行的有技巧。于是,他们决定举办一场比赛,比赛的地方将在一个迷宫之中。

这些鹰的起始点被设在一个 n ∗ m n * m nm 矩阵的左下角 m a p 1 , 1 map_{1,1} map1,1 的左下角。终点被设定在矩阵的右上角 m a p n , m map_{n, m} mapn,m 的右上角,有些 m a p i , j map_{i, j} mapi,j 是可以从中间穿越的。每一个方格的边长都是 100 100 100 米。如图所示:

在这里插入图片描述
没有障碍,也没有死路。这样设计主要是为了高速飞行的鹰们不要发现死路来不及调整而发生意外。潘帕斯雄鹰冒着减RP的危险从比赛承办方戒备森严的基地中偷来了施工的地图。但是问题也随之而来,他必须在比赛开始之前把地图的每一条路都搞清楚,从中找到一条到达终点最近的路。(哈哈,笨鸟不先飞也要拿冠军)但是此鹰是前无古鹰,后无来鹰的吃菜长大的鹰–菜鸟。他自己没有办法得出最短的路径,于是紧急之下找到了学 O I OI OI 的你,希望找到你的帮助。

输入格式

首行为 n , m n, m n,m ,第 2 2 2 行为 k k k 表示有多少个特殊的边。以下 k k k 行为两个数, i , j i, j i,j 表示 m a p i , j map_{i,j} mapi,j 是可以直接穿越的。

输出格式

仅一行, ( 1 , 1 ) → ( n , m ) (1, 1) \to (n, m) (1,1)(n,m) 的最短路径的长度,四舍五入保留到整数即可。

样例

样例输入1:

3 2
3
1 1
3 2
1 2

样例输出1:

383

数据范围

0 ≤ n , m ≤ 1 0 5 0 \le n, m \le 10^5 0n,m105
0 ≤ k ≤ 1000 0 \le k \le 1000 0k1000

题解

题目要求 ( 1 , 1 ) → ( n , m ) (1,1) \to (n, m) (1,1)(n,m) 的最短路径。

考虑这种问题是否能向回走(即向左和向下更优)。
如果在 ( i , j ) (i, j) (i,j) 向左或向下走,则必有两条无用的线段(走出去和返回)。
即使两条中有用到特殊路段,将去和返回的两条线段拼在一起,两边之和大于第三边(可以直接到达的线段)。
因此只能向上和向右走。

考虑使用 d p dp dp 或记忆化搜索进行处理:

很容易想到一个暴力的方法:
当前状态为 ( i , j ) (i, j) (i,j),可以从 ( i − 1 , j ) (i - 1, j) (i1,j) ( i , j − 1 ) (i, j - 1) (i,j1) 走过来。如果是特殊路段,还可以从 ( i − 1 , j − 1 ) (i - 1, j - 1) (i1,j1) 走过来。
时间复杂度为 O ( n × m ) O(n \times m) O(n×m)

注意到 n × m n \times m n×m 很大,但 k k k 很小。
如果没有特殊路段, ( 1 , 1 ) → ( n , m ) (1, 1) \to (n, m) (1,1)(n,m) 的最短路径长度一定是 n + m n + m n+m
由于特殊路段比两条路加起来短,所以只需求替换满足条件的最多个数的特殊路段。

如果要走到 ( i , j ) (i, j) (i,j),则之前走过的 ( x , y ) (x, y) (x,y) 一定满足 x ≤ i x \le i xi y ≤ j y \le j yj
也就是说,如果要走特殊路段 ( p , q ) (p, q) (p,q),上一个选的特殊路段 ( x , y ) (x, y) (x,y) 一定满足 x < p x < p x<p y < q y < q y<q
即对特殊路段求最长上升子序列。

先按照 x x x 坐标进行排序,在对 y y y 求最长上升子序列(注意要判断 x x x 的大小),就求出了替换个数 s s s
答案就为 n + m − s × ( 2 − 2 ) n + m - s \times (2 - \sqrt2) n+ms×(22 ),四舍五入即可。

sort(a + 1, a + k + 1, cmp);//x 从小到大
//最长上升子序列
for(int i = 1; i <= k; ++ i){
	f[i] = 1;
	for(int j = 1; j < i; ++ j){
		if(a[i].y > a[j].y && a[i].x > a[j].x){
			f[i] = max(f[j] + 1, f[i]);
		}
	}
}
int ans = 0;
for(int i = 1; i <= k; ++ i){
	ans = max(ans, f[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值