P3842 [TJOI2007]线段
作者:互联网
题意:
有\(n*n\)的区间,你从(1,1)点出发,到达(n,n)点,每一行有条线段,你需要走完每一条线段,问最少需要走几步?(\(n\leq2*10^4\))
题解:
这是个很裸的\(DP\)(最近重新开始刷\(DP\)),显然开\(n*n\)的区间是开不了的,我们知道,每一行结束的位置一定在每一行线段的左端点或者右端点,即恰好走完这条线段所在的位置,那么开\(dp[n][2]\)的空间就可以了,\(dp[i][0]\)表示到达第\(i\)条线段的左端点所用的最短距离,\(dp[i][1]\)表示到达第\(i\)条线段的右端点的最短距离,转移的话可以从上条线段的左端点和右端点转移过来,取\(min\)就可以了。
#include "bits/stdc++.h"
using namespace std;
const int inf=0x3f3f3f3f;
const int N=2e4+5;
int n;
int l[N],r[N],ans,dp[N][2];
void DP(int i){
if(l[i-1]>r[i]) dp[i][0]=min(dp[i][0],dp[i-1][0]+l[i-1]-l[i]),dp[i][1]=min(dp[i][1],dp[i-1][0]+l[i-1]+r[i]-2*l[i]);
if(l[i-1]<l[i]) dp[i][0]=min(dp[i][0],dp[i-1][0]+2*r[i]-l[i]-l[i-1]),dp[i][1]=min(dp[i][1],dp[i-1][0]+r[i]-l[i-1]);
if(l[i-1]>=l[i]&&l[i-1]<=r[i]) dp[i][0]=min(dp[i][0],dp[i-1][0]+2*r[i]-l[i]-l[i-1]),dp[i][1]=min(dp[i][1],dp[i-1][0]+r[i]+l[i-1]-2*l[i]);
if(r[i-1]>r[i]) dp[i][0]=min(dp[i][0],dp[i-1][1]+r[i-1]-l[i]),dp[i][1]=min(dp[i][1],dp[i-1][1]+r[i-1]+r[i]-2*l[i]);
if(r[i-1]<l[i]) dp[i][0]=min(dp[i][0],dp[i-1][1]+2*r[i]-l[i]-r[i-1]),dp[i][1]=min(dp[i][1],dp[i-1][1]+r[i]-r[i-1]);
if(r[i-1]>=l[i]&&r[i-1]<=r[i]) dp[i][0]=min(dp[i][0],dp[i-1][1]+2*r[i]-l[i]-r[i-1]),dp[i][1]=min(dp[i][1],dp[i-1][1]+r[i]+r[i-1]-2*l[i]);
}
int main(){
cin>>n;
memset(dp,inf,sizeof dp);
for(int i=1;i<=n;i++)
scanf("%lld %lld",&l[i],&r[i]);
int pos=1;
l[0]=r[0]=1;
dp[0][0]=dp[0][1]=0;
for(int i=1;i<=n;i++){
DP(i);
if(i!=n) dp[i][0]++,dp[i][1]++;
}
ans=min(dp[n][0]+n-l[n],dp[n][1]+n-r[n]);
cout<<ans;
return 0;
}
标签:P3842,min,int,线段,DP,端点,TJOI2007,dp 来源: https://www.cnblogs.com/monster-Z/p/14409319.html