[TJOI2007]线段

题目描述

在一个 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 ;  
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值