HDU 1300 Pearls 斜率DP 入门

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

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

综上,假如 sijsjks_{ij}\le s_{jk} ,那么 jj 必定不是最优解,可以舍去。
因此维护的最优解序列是一个斜率单增的序列,可以用单调队列进行维护。
当选择最优解给 dp[i]dp[i] 时,在队列头部依次找第一个斜率不符合 (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;
}

在这里插入图片描述

posted @ 2020-03-12 10:30  winechord  阅读(70)  评论(0编辑  收藏  举报