HDU 1300 Pearls 斜率DP 入门
HDU 1300 Pearls
给 个种类的珍珠,每个有需要的数量以及对应的价格,购买某种类时需要额外再加十个,可以通过购买高种类的来替代低种类的。
可以得到状态转移方程:
其中 表示前 个类别购买的最优值(最小价格), 表示第 个种类的价格,用 表示第 个种类所需的数量, 则为 的前缀和。
此状态转移方程的意思是:前 个类别的最优值等于前 个类别的最优值加上类别 到类别 都用 类价格购买的花费,枚举 取最小。
很有斜率DP的味道,不妨来推一推,首先假设 ,假如 优于 的话,那么有:
左边是一个斜率的形式,其中 ,也就是说当斜率小于 时, 优于 。
现在再考虑,对于一个给定的 ,假如 优于 ,由于 ,也就是说对于 ,即计算 时,上述不等式依然成立, 仍然是优于 的。
然后令斜率 ,现有结论:对于 ,假如 ,那么 必定不是最优解,可以舍去,原因如下:
- 首先假如 ,那么 优于 ,故 可以舍去;
- 其次假如 ,那么 ,则 优于 故 可以舍去。
综上,假如 ,那么 必定不是最优解,可以舍去。
因此维护的最优解序列是一个斜率单增的序列,可以用单调队列进行维护。
当选择最优解给 时,在队列头部依次找第一个斜率不符合 式的点进行赋值。
详见代码:
#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;
}