[TJOI2007] 线段
因为每行必须走完才能到下一行,所以我们有两种决策:
1、最后留在线段左端点
2、最后留在线段右端点
这种存在状态转移且多决策的问题用动态规划来进行递推是最好不过的了。
所以我们设\(dp[i][0/1]\)来表示在第\(i\)行最后留在左/右端点的行走路径最小值。然后设\(sum[0/1][0/1]\)来表示相邻行左右端点之间的距离。(0表示左端点,1表示右端点)
然后很容易就知道状态转移的式子:
\(dp[i][0]=min(dp[i-1][0]+1+dis[0][0],dp[i-1][1]+1+dis[1][0])\)
\(dp[i][1]=min(dp[i-1][0]+1+dis[0][1],dp[i-1][1]+1+dis[1][1])\)
然后就是注意相邻两行左右端点之间的距离是存在3*2种分类讨论情况的(具体操作见代码)。
我的思路可能有点麻烦了,所以代码写的也有点长,但是自我认为超级暴力超级清楚。。。。。
以下是代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 20010
#define int long long
using namespace std;
int n;
int dp[MAXN][2],dis[2][2],l[MAXN],r[MAXN];
//dis[0][0] left->left
//dis[0][1] left->right
//dis[1][0] right->left
//dis[1][1] right->right
int ans;
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&l[i],&r[i]);
l[0]=r[0]=1;
l[n+1]=r[n+1]=n;
for(int i=1;i<=n+1;i++)
{
if(l[i-1]<l[i])
dis[0][0]=r[i]-l[i-1]+r[i]-l[i],dis[0][1]=r[i]-l[i-1];
else if(l[i-1]>r[i])
dis[0][0]=l[i-1]-l[i],dis[0][1]=r[i]-l[i]+l[i-1]-l[i];
else
dis[0][0]=2*r[i]-l[i-1]-l[i],dis[0][1]=l[i-1]-l[i]+r[i]-l[i];
//the last position of the point is on the left
if(r[i-1]<l[i])
dis[1][0]=r[i]-r[i-1]+r[i]-l[i],dis[1][1]=r[i]-r[i-1];
else if(r[i-1]>r[i])
dis[1][0]=r[i-1]-l[i],dis[1][1]=r[i-1]-l[i]+r[i]-l[i];
else
dis[1][0]=r[i]-r[i-1]+r[i]-l[i],dis[1][1]=r[i-1]-l[i]+r[i]-l[i];
//the last position of the point is on the right
dp[i][0]=min(dp[i-1][0]+1+dis[0][0],dp[i-1][1]+1+dis[1][0]);
dp[i][1]=min(dp[i-1][0]+1+dis[0][1],dp[i-1][1]+1+dis[1][1]);
}
printf("%lld\n",min(dp[n+1][0],dp[n+1][1])-2);
return 0;
}