题解 P3842 【[TJOI2007]线段】
P3842 【[TJOI2007]线段】
思路
不难看出,路程长度由两个部分组成:左右走的距离
很明显,当我们走完一行的线段时,我们会处在左端点/右端点,这样我们就需要在线性
所以我们定义一个
然后我们就可以用上一行的状态来推出下一行了。
分为
上一行在左端点,下一行走到左端点; 上一行在左端点,下一行走到右端点; 上一行在右端点,下一行走到左端点; 上一行在右端点,下一行走到右端点;
我们以"上一行在右端点,下一行走到右端点"为例:
这样我们就可以推出状态转移方程了。
f[i][1]=min(f[i-1][1]+abs(l[i]-r[i-1])+r[i]-l[i]+1,f[i-1][0]+abs(l[i]-l[i-1])+r[i]-l[i]+1);//本行走到右端点,上一行走到左/右端点,取最小值
f[i][0]=min(f[i-1][1]+abs(r[i]-r[i-1])+r[i]-l[i]+1,f[i-1][0]+abs(r[i]-l[i-1])+r[i]-l[i]+1);//本行走到左端点,上一行走到左/右端点,取最小值
最后不要忘了初始化第
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e4+50;
int n;
int l[maxn],r[maxn];
int f[maxn][2];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&l[i],&r[i]);
}
f[1][0]=2*r[1]-l[1]-1;//第1行往返,走到右端点再回来
f[1][1]=r[1]-1;//直接走到右端点
for(int i=2;i<=n;i++){
f[i][1]=min(f[i-1][1]+abs(l[i]-r[i-1])+r[i]-l[i]+1,f[i-1][0]+abs(l[i]-l[i-1])+r[i]-l[i]+1);//本行走到右端点,上一行走到左/右端点,取最小值
f[i][0]=min(f[i-1][1]+abs(r[i]-r[i-1])+r[i]-l[i]+1,f[i-1][0]+abs(r[i]-l[i-1])+r[i]-l[i]+1);//本行走到左端点,上一行走到左/右端点,取最小值
}
printf("%d\n",min(f[n][1]+n-r[n],f[n][0]+n-l[n]));//因为最后还要走到(n,n)的地方,加上剩下的距离,再加上上下走的距离即可
return 0;
}