题目描述
在一个 n*n 的平面上,在每一行中有一条线段,第 i 行的线段的左端点是(i, L(i)),右端点是(i, R(i)),其中 1 ≤ L(i) ≤ R(i) ≤ n。
你从(1, 1)点出发,要求沿途走过所有的线段,最终到达(n, n)点,且所走的路程长度要尽量短。
更具体一些说,你在任何时候只能选择向下走一步(行数增加 1)、向左走一步(列数减少 1)或是向右走一步(列数增加 1)。当然,由于你不能向上行走,因此在从任何一行向下走到另一行的时候,你必须保证已经走完本行的那条线段。
输入输出格式
输入格式:
输入文件的第一行有一个整数 n,以下 n 行,在第 i 行(总第(i+1)行)的两个整数表示
L(i)和 R(i)。
输出格式:
输出文件仅包含一个整数,你选择的最短路程的长度。
输入输出样例
输入样例#1:
6
2 6
3 4
1 3
1 2
3 6
4 5
输出样例#1:
24
说明
我们选择的路线是
(1,1) (1,6)
(2,6) (2, 3)
(3, 3) (3, 1)
(4, 1) (4, 2)
(5, 2) (5, 6)
(6, 6) (6, 4) (6, 6)
不难计算得到,路程的总长度是 24。 100%的数据中,n ≤ 20 000。
因为这一道题的题目要求要走完全部的线段,注意不是走过,是整条线段走完
所以我们不能用贪心来做 , 经过观察,可以发现
往下走的都是到达了本行线段的某一个端点并且走完了这一个线段,所以dp只用继承前一条线段的两个端点
代码 (用滚动数组优化)
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std ;
const int N = 2e4 + 10 ;
int n , zb[2][2] ; //zb[][0]表示线段的左端点的位置,zb[][1]表示线段右端点的位置
int dp[2][2] ;
int main() {
memset ( dp , 63 , sizeof ( dp ) ) ;
cin >> n >> zb[1][0] >> zb[1][1] ;
dp[1][1] = zb[1][1] - 1 ;
for ( int i = 2 ; i <= n ; i ++ ) {
cin >> zb[i&1][0] >> zb[i&1][1] ;
for ( int j = 0 ; j < 2 ; j ++ ) //左右两端点分别继承
dp[i&1][j] = min ( //继承左右端点,先要从继承的那个点下来,然后到另一个点去,再到这个点
dp[i&1^1][0] + abs ( zb[i&1][1-j] - zb[i&1^1][0] ) ,
dp[i&1^1][1] + abs ( zb[i&1][1-j] - zb[i&1^1][1] )
) + 1 + zb[i&1][1] - zb[i&1][0] ;
}
cout << min ( dp[n&1][0] + abs( n - zb[n&1][0] ) , dp[n&1][1] + abs ( n - zb[n&1][1] ) ) << endl ;
return 0 ;
}