其他分享
首页 > 其他分享> > P3842 [TJOI2007]线段

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