URAL 2072 Kirill the Gardener 3 (单调DP)

【题目链接】 http://acm.timus.ru/problem.aspx?space=1&num=2072

【题目大意】

  一个园丁要给一排花浇水,每个花都有一个标号,必须要先浇标号小的,
  移动一格需要一单位时间,浇花需要一单位时间,园丁一开始在最左边的花处,
  问浇完所有花的最短时间

【题解】

  同一标号的花从开始浇水到结束,最优的一定是从最左边走到最右边
  或者从最右边到最左边,所以我们将每种标号的花的最左和最右作为状态做单调dp,
  就能得到答案。

【代码】

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int MAX_N=200010;
int L[MAX_N],R[MAX_N];
long long dp[MAX_N][2];
int n,m,s[MAX_N],disc[MAX_N],a[MAX_N];
int remark(int x){
	  int l=1,r=m;
	  while(l<=r){
		    int mid=(l+r)>>1;
		    if(disc[mid]<x)l=mid+1;
		    else if(disc[mid]==x)return mid;
		    else r=mid-1;
	  }
}
int main(){
    while(~scanf("%d",&n)){
    	memset(s,0,sizeof(s));
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),disc[i]=a[i];
        sort(disc+1,disc+n+1);
        m=unique(disc+1,disc+n+1)-(disc+1);
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<=n;i++)a[i]=remark(a[i]);
        for(int i=1;i<=n;i++){
            if(!L[a[i]])L[a[i]]=i;
            R[a[i]]=i;
            s[a[i]]++;
        }L[0]=1; R[0]=1;
        dp[0][0]=dp[0][1]=0;
        for(int i=1;i<=m;i++){
            dp[i][0]=min(dp[i][0],dp[i-1][0]+s[i]+abs(R[i]-L[i-1])+R[i]-L[i]);
            dp[i][0]=min(dp[i][0],dp[i-1][1]+s[i]+abs(R[i]-R[i-1])+R[i]-L[i]);
            dp[i][1]=min(dp[i][1],dp[i-1][0]+s[i]+abs(L[i]-L[i-1])+R[i]-L[i]);
            dp[i][1]=min(dp[i][1],dp[i-1][1]+s[i]+abs(L[i]-R[i-1])+R[i]-L[i]);
        }printf("%lld
",min(dp[m][1],dp[m][0]));
    }return 0;
}
原文地址:https://www.cnblogs.com/forever97/p/ural2072.html