题意:给你一些台阶的高度,使得相邻两个台阶之间的高度差不大于d(1<d<1e9),问至少需要增加或减少的高度和是多少。
分析:如果按照常规DP想总共有O(N*d)种状态,无解问题。找一找规律,当n=3时,第二个台阶只能修改max(h1,h3)-d,min(h1,h3)+d,h2.中的一种才能保证修改最小。然后可以推出每个台阶转移高度只能为hi+kd(-n<k<n)
状态就减少到O(n*n*n)种。然后推出状态转移方程dp[i][j]=|hi-j|+min(dp[i-1][k])(x[j]-d<=x[k]<=x[j]+d);看到这个式子。就可以想到用单调队列优化啦。(详见紫书P296)
附上代码
#include <stdio.h> #include <algorithm> #include <queue> #include <string.h> #include <math.h> #include <iostream> #include <math.h> #include <queue> #include <vector> using namespace std; typedef long long ll; const ll INF=((1LL)<<60); ll d,x[80100],h[111],dp[2][30010]; int n; int main() { int T_T; scanf("%d",&T_T); while(T_T--) { scanf("%d%lld",&n,&d); for(int i=1;i<=n;i++) scanf("%lld",&h[i]); if(abs(h[1]-h[n])>(d*(ll)(n-1))) { printf("impossible\n"); continue; } int m=0; for(int i=1;i<=n;i++) for(ll j=-n;j<=n;j++) x[m++]=h[i]+j*d; sort(x,x+m); m=unique(x,x+m)-x; int t=1; for(int i=0;i<m;i++) { dp[1][i]=INF; if(x[i]==h[1]) dp[1][i]=0; } for(int i=2;i<=n;i++) { int k=0; for(int j=0;j<m;j++) { while(k<m&&x[k]<(x[j]-d))k++; while(k+1<m&&x[k+1]<=(x[j]+d)&&dp[t][k+1]<=dp[t][k])k++; ll vv=dp[t][k]; if(vv==INF) dp[t^1][j]=INF; else dp[t^1][j]=abs(h[i]-x[j])+vv; } t^=1; } ll ans=0; for(int j=0;j<m;j++) { if(x[j]==h[n]) { ans=dp[t][j]; break; } } printf("%lld\n",ans); } return 0; }