HDU 1300 Pearls 斜率DP 入门

HDU 1300 Pearls
c c c 个种类的珍珠,每个有需要的数量以及对应的价格,购买某种类时需要额外再加十个,可以通过购买高种类的来替代低种类的。
可以得到状态转移方程:
d p [ i ] = min ⁡ { d p [ k ] + p [ i ] × ( s [ i ] − s [ k ] + 10 ) } , k ∈ [ 1 , i − 1 ] dp[i]=\min\{dp[k]+p[i]\times(s[i]-s[k]+10)\}, k\in [1,i-1] dp[i]=min{dp[k]+p[i]×(s[i]s[k]+10)},k[1,i1]
其中 d p [ i ] dp[i] dp[i] 表示前 i i i 个类别购买的最优值(最小价格), p [ i ] p[i] p[i] 表示第 i i i 个种类的价格,用 a [ i ] a[i] a[i] 表示第 i i i 个种类所需的数量, s [ i ] s[i] s[i] 则为 a [ i ] a[i] a[i] 的前缀和。
此状态转移方程的意思是:前 i i i 个类别的最优值等于前 k k k 个类别的最优值加上类别 k + 1 k+1 k+1 到类别 i i i 都用 i i i 类价格购买的花费,枚举 k k k 取最小。
很有斜率DP的味道,不妨来推一推,首先假设 k < j < i k<j<i k<j<i,假如 j j j 优于 k k k 的话,那么有:
d p [ j ] + p [ i ] × ( s [ i ] − s [ j ] + 10 ) < d p [ k ] + p [ i ] × ( s [ i ] − s [ k ] + 10 ) d p [ j ] − d p [ k ] < p [ i ] ( s [ j ] − s [ k ] ) d p [ j ] − d p [ k ] s [ j ] − s [ k ] < p [ i ] y j − y k x j − x k < p [ i ] (1) \begin{aligned} dp[j]+p[i]\times (s[i]-s[j]+10)&<dp[k]+p[i]\times(s[i]-s[k]+10)\\ dp[j]-dp[k]&<p[i](s[j]-s[k])\\ \frac{dp[j]-dp[k]}{s[j]-s[k]}&<p[i]\\ \frac{y_j-y_k}{x_j-x_k}&<p[i]\tag{1} \end{aligned} dp[j]+p[i]×(s[i]s[j]+10)dp[j]dp[k]s[j]s[k]dp[j]dp[k]xjxkyjyk<dp[k]+p[i]×(s[i]s[k]+10)<p[i](s[j]s[k])<p[i]<p[i](1)
左边是一个斜率的形式,其中 y j = d p [ j ] , x j = s [ j ] y_j=dp[j],x_j=s[j] yj=dp[j],xj=s[j] ,也就是说当斜率小于 p [ i ] p[i] p[i] 时, j j j 优于 k k k
现在再考虑,对于一个给定的 i i i ,假如 j j j 优于 k k k ,由于 p [ i + 1 ] > p [ i ] p[i+1]>p[i] p[i+1]>p[i] ,也就是说对于 i + 1 i+1 i+1,即计算 d p [ i + 1 ] dp[i+1] dp[i+1] 时,上述不等式依然成立, j j j 仍然是优于 k k k 的。
然后令斜率 s j k = y j − y k x j − x k s_{jk}=\dfrac{y_j-y_k}{x_j-x_k} sjk=xjxkyjyk,现有结论:对于 k < j < i k<j<i k<j<i ,假如 s i j ≤ s j k s_{ij}\le s_{jk} sijsjk ,那么 j j j 必定不是最优解,可以舍去,原因如下:

  1. 首先假如 s i j < p [ i ] s_{ij}<p[i] sij<p[i],那么 i i i 优于 j j j ,故 j j j 可以舍去;
  2. 其次假如 s i j ≥ p [ i ] s_{ij}\ge p[i] sijp[i] ,那么 s j k ≥ p [ i ] s_{jk}\ge p[i] sjkp[i] ,则 k k k 优于 j j j j j j 可以舍去。

综上,假如 s i j ≤ s j k s_{ij}\le s_{jk} sijsjk ,那么 j j j 必定不是最优解,可以舍去。
因此维护的最优解序列是一个斜率单增的序列,可以用单调队列进行维护。
当选择最优解给 d p [ i ] dp[i] dp[i] 时,在队列头部依次找第一个斜率不符合 ( 1 ) (1) (1) 式的点进行赋值。
详见代码:

#include<iostream>
//#define WINE
#define MAXN 110
using namespace std;
int T,c,a[MAXN],p[MAXN],s[MAXN],h,t,q[MAXN],dp[MAXN];
int up(int j,int k){
    return dp[j]-dp[k];
}
int down(int j,int k){
    return s[j]-s[k];
}
int getDP(int i,int k){
    return dp[k]+p[i]*(s[i]-s[k]+10);
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d",&T);
    while(T--){
        scanf("%d",&c);
        for(int i=1;i<=c;i++){
            scanf("%d%d",&a[i],&p[i]);
            s[i]=s[i-1]+a[i];
            //dp[i]=(a[i]+10)*p[i];
        }
        h=t=0;q[t++]=0;
        for(int i=1;i<=c;i++){
            while(h+1<t&&up(q[h+1],q[h])<p[i]*down(q[h+1],q[h]))
                h++;
            dp[i]=getDP(i,q[h]);
            while(h+1<t&&up(i,q[t-1])*down(q[t-1],q[t-2])<up(q[t-1],q[t-2])*down(i,q[t-1]))
                t--;
            q[t++]=i;
        }
        printf("%d\n",dp[c]);
    }
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值