我要长高 (dp加优化)
题目链接:我要长高
题意是……就是题目描述的那样了吧 题意很显而易见,一眼dp题
dp[i][j]代表第i个人身高为j时的最小消耗,
dp[i][j] = min (dp[i-1][k] + abs(j - k ) * C + (j - h[i])^2)
复杂度O(n * h[i]^2 ),很明显有点大,学习了一下单调队列优化dp之后,发现形如dp[i] = min/max (dp[j]) + x[i]的方程,一般可以用单调队列优化。
然后我们试着把方程变形一下
当第i个人身高比前一个高的时候
dp[i][j] = min( dp[i-1][k] - k * C + j * C + (j - h[i]) ^ 2 ) (1 <= k <= j)
那么令F[i-1][k] = dp[i-1][k] - k * C , X[i][j] = j * C + (j - h[i]) ^ 2 (先忽略i这个变量),则原方程可以看成dp[j] = min(F[k]) + X[j] ,因此可以用单调队列来优化,不过这里的k取值范围是(1, j) ,所以即使不用单调队列也是可以的,直接令f[i][j] 为 min(dp[i][k] - k * C) 直接递推就行
当第i个人身高比前一个矮的时候同理。令f[i][j]为j到100里面dp[i][k] + k * C的最小值,倒着递推即可。
但是有时候如果对k的取值范围有所限制,(比如限定某一固定长度的区间时)就需要用到单调队列了。
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define LONG long long
const int INF=0x3f3f3f3f;
const int MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson l , mid , rt<< 1
#define rson mid + 1 ,r , (rt<<1)+1
#define root 1, m , 1
int dp[2][110] ;
int h[51000] ;
int f[2][110] ;
int f2[2][110] ;
int main()
{
int n ;
int C ;
while(~scanf("%d",&n))
{
scanf("%d",&C) ;
for(int i = 1; i <= n ; ++i)
scanf("%d",&h[i]) ;
clr1(dp) ;
clr1(f) ;
clr1(f2) ;
//爆空间了,所以用滚动数组。
int now = 0 , pre = 1 ;
for(int j = h[1] ; j <= 100 ; ++ j)
dp[1][j] = (j - h[1]) * (j - h[1]) ;
for( int j = 1 ; j <= 100 ; ++ j)
f[1][j] = min (f[1][j-1] , dp[1][j] - j * C) ;
for(int j = 100 ; j >= 1 ; -- j)
f2[1][j] = min(f2[1][j+1] , dp[1][j] + j * C) ;
for(int i = 2;i <= n ; ++ i)
{
clr1(dp[now]) ;
clr1(f[now]) ;
clr1(f2[now]) ;
for(int j = h[i] ; j <= 100 ; ++ j)
dp[now][j] = min( dp[now][j] , (j - h[i]) * (j - h[i]) + j * C + f[pre][j] );
for(int j = 100 ; j >= h[i] ; -- j)
dp[now][j] = min(dp[now][j] , (j - h[i])* (j - h[i]) - j * C + f2[pre][j]) ;
for(int j = 1 ; j <= 100 ; ++j)
f[now][j] = min(f[now][j-1] , dp[now][j] - j * C );
for(int j = 100 ; j >= 1 ; -- j)
f2[now][j] = min (f2[now][j + 1] , dp[now][j] + j * C ) ;
now ^= 1 ;
pre ^= 1 ;
}
int ans = INF ;
for(int i = h[n] ; i <= 100 ; ++ i)ans = min(ans , dp[pre][i]) ;
printf("%d\n",ans) ;
}
}