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\n",min(dp[m][1],dp[m][0])); }return 0; }
愿你出走半生,归来仍是少年