suxxsfe

一言(ヒトコト)

P3842 [TJOI2007]线段

最近多刷些dp,觉得这个算不上蓝题
 
在一个\(n\times n\)的平面上,在每一行中有一条线段,第\(i\)行的线段的左端点是\((i, L_i)\),右端点是\((i, R_i)\),其中\(1\leq L_i \leq R_i \leq n\)
你从\((1, 1)\)点出发,要求沿途走过所有的线段,最终到达\((n, n)\)点,且所走的路程长度要尽量短。
更具体一些说,你在任何时候只能选择向下走一步(行数增加 1)、向左走一步(列数减少 1)或是向右走一步(列数增加 1)。当然,由于你不能向上行走,因此在从任何一行向下走到另一行的时候,你必须保证已经走完本行的那条线段。
 


设计状态,每行走完都会停留在左端点或是右端点,因为不能走不完在中间就停下
所以考虑将状态设计为\(f_{i,0/1}\),表示走完了第\(i\)行,并停留在左端点/右端点
预处理出第一行的值
以每一行的\(f_{i,0}\)(走完停留在左端点)为例,因为要停留在左端点,肯定要从右端点开始走
如果上一行是停留在右端点,则走到右端点的代价为\(\text{abs}(R_{i-1}-R_i)+1+f_{i-1,1}\)
同样,上一行停留在左端点,走到这一行右端点的代价为\(\text{abs}(L_{i-1}-R_i)+1+f_{i-1,0}\)
让后当然还要再加上这一行线段的长度
每行的\(f_{i,1}\)也是同理
答案显然是\(\min(f_{n,0}+(n-L_n),f_{n,1}+(n-R_n))\),别忘了最后还要走到\((n,n)\)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	int x=0,y=1;
	char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n;
LL f[20006][2];
LL l[20006],r[20006];
int main(){
	n=read();
	for(reg int i=1;i<=n;i++) l[i]=read(),r[i]=read();
	f[1][0]=r[1]-1+(r[1]-l[1]);f[1][1]=r[1]-1;
	for(reg int i=2;i<=n;i++){
		f[i][0]=std::min(f[i-1][0]+std::abs(l[i-1]-r[i])+(r[i]-l[i]),
			f[i-1][1]+std::abs(r[i-1]-r[i])+(r[i]-l[i]))+1;
		f[i][1]=std::min(f[i-1][0]+std::abs(l[i-1]-l[i])+(r[i]-l[i]),
			f[i-1][1]+std::abs(r[i-1]-l[i])+(r[i]-l[i]))+1;
	}
	std::printf("%lld",std::min(f[n][0]+n-l[n],f[n][1]+n-r[n]));
	return 0;
}
posted @ 2020-03-19 21:28  suxxsfe  阅读(146)  评论(0编辑  收藏  举报