UESTC 我要长高

我要长高 (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) ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值