(DP004)洛谷P3842 [TJOI2007]线段

一、算法分析

这道题乍一看挺没有头绪的,所以先多读两遍题目,把样例的草图画下来,如下图所示,注:题目中的坐标系为算法题常见的坐标系画法,详见下图:
(DP004)洛谷P3842 [TJOI2007]线段_第1张图片
然后首先想到,因为不能后退,所以答案与纵坐标无关,只需把所有的横向移动的长度算好之后,最后加上n-1即可。进一步可以想到,可以将纵坐标看做是阶段,将对于左右端点的选择看做是决策,用DP求解最优化问题。即可得:f[i][0]表示当前位于第i行的线段的左端点时的最短路(只计算横向,下同),f[i][1]则表示当前位于第i行的线段的右端点时的最短路。然后再看一下题目的数据范围是20000,可验证这种做法的时间和空间复杂度可行。
接下来先画图分析,分析清楚之后再写代码
以下为f[i][0]的状态转移方式
(DP004)洛谷P3842 [TJOI2007]线段_第2张图片
f[i][1]以同样的方式分析(详见代码)

二、代码及注释

#include
#include
#include
#include
using namespace std;
const int N=20050;
int f[N][2];                              //每一行的线段,当前点位于左侧(0)或右侧(1)时的最小值
int n;
int L[N],R[N];
int main(){
    
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&L[i],&R[i]);
    }
    f[1][0]=R[1]-L[1]+R[1]-1;
    f[1][1]=R[1]-1;                         //先处理好边界,然后下面分情况dp即可
    for(int i=2;i<=n;i++){
        int a,b;
        if(L[i-1]<R[i]) a=f[i-1][0]+R[i]-L[i-1]+R[i]-L[i];
        else a=f[i-1][0]+L[i-1]-L[i];
        if(R[i-1]<R[i]) b=f[i-1][1]+R[i]-R[i-1]+R[i]-L[i];
        else b=f[i-1][1]+R[i-1]-L[i];
        f[i][0]=min(a,b);
        if(L[i-1]<=L[i]) a=f[i-1][0]+R[i]-L[i-1];
        else a=f[i-1][0]+L[i-1]-L[i]+R[i]-L[i];
        if(R[i-1]<=L[i]) b=f[i-1][1]+R[i]-R[i-1];
        else b=f[i-1][1]+R[i-1]-L[i]+R[i]-L[i];
        f[i][1]=min(a,b);
    }
    
    int ans=min(f[n][0]+n-L[n],f[n][1]+n-R[n]);
    ans+=n-1;                                //加上纵向的
    printf("%d",ans);
    
    return 0;
    
}

你可能感兴趣的:(递推&动态规划)