题目大意:有n种珠宝,每件珠宝有必须要买的数量ai和单价pi,c种珠宝的单价递增。如果买了某种珠宝,需要额外付一次10*pi的费用,同时允许用等量的高价珠宝代替等量的的低价珠宝,问至少要花多少钱,才能达到要求
解题思路:设 dp[i]为买前i种珠宝需要花费的最小代价
得转移方程dp[i] = dp[j] + (sum[i] - sum[j] + 10) * val[i]
sum[i]指前i中珠宝的数量和
假设k > j,且k点比j点更优
则 dp[j] + (sum[i] - sum[j] + 10 ) * val[i] >= dp[k] + (sum[i] - sum[k] + 10) * val[i]
化简可得val[i] >= (dp[k] - dp[j]) / (sum[k] - sum[j])
因为价格递增,所以得到优化方程
#include <cstdio>
#include <cstring>
const int N = 110;
int sum[N], val[N], dp[N], que[N];
int n;
void init() {
dp[0] = sum[0] = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &sum[i], &val[i]);
sum[i] += sum[i - 1];
}
}
int getUp(int j, int k) {
return dp[j] - dp[k];
}
int getDown(int j, int k) {
return sum[j] - sum[k];
}
int getDp(int i, int j) {
return dp[j] + (sum[i] - sum[j] + 10) * val[i];
}
void solve(){
int head, tail;
head = tail = 0;
que[tail++] = 0;
for (int i = 1; i <= n; i++) {
while (head + 1 < tail && getUp(que[head + 1], que[head]) <= getDown(que[head + 1], que[head]) * val[i])
head++;
dp[i] = getDp(i, que[head]);
while (head + 1 < tail && getUp(i, que[tail - 1]) * getDown(que[tail - 1], que[tail - 2]) <= getUp(que[tail - 1], que[tail - 2]) * getDown(i, que[tail - 1]))
tail--;
que[tail++] = i;
}
printf("%d\n", dp[n]);
}
int main() {
int test;
scanf("%d", &test);
while (test--) {
init();
solve();
}
return 0;
}