【动态规划】 Little Sub and Piggybank
【动态规划】 Little Sub and Piggybank
Problem
Little Sub has a very special piggybank.
Every day i, the supermarket will sell a set of special candies which is only available at that specific day. It costs W[i] and will bring V [i] happiness to Little Sub.
However, Little Sub can buy the candy set only when there is exactly W[i] money in his piggybank.
If he chooses to buy it, the piggybank will be empty again.
Initially, there is no money in the piggybank. Little Sub can put any real number amount of money into it everyday before he decides whether to buy the candy. What’s more, one condition he should follow is that he cannot put more money into the piggybank than yesterday. One special example is that if you don’t put money into the piggybank this time, you cannot put money into the piggybank anymore.
Now we have already know the selling plan in the following n days and the piggybank is empty now.
Please help Little Sub to maximize the happiness he gets.
Input
The first line contains one positive integer n(1 ≤ n≤2000).
The second line contains n integers Wi(0≤Wi≤106).
The third line contains n integers Vi(0≤Vi≤106).
Output
Please output an integer in one line, indicating the maximum happiness that Little Sub can achieve.
Sample Input
4
0 2 4 1
0 4 3 1
Sample Output
5
Solution
你有一个piggybank小猪存钱罐。你在每天的开始都可以向piggybank中放入任意数量的钱,只要放的不比前一天多就行。第一天可以放入任何数量的钱。每天超市都有一个物品,第i
天的物品价格为w[i]
,购买后得到的愉悦度为v[i]
。但是你只有在piggybank中的钱刚好等于w[i]
时,才可以买入第i
天的物品,买完之后你的钱包就空了。问到第n
天,能够获得最大愉悦度是多少。
dp[i][j]
表示当前购买的是i
物品,前一个是j
物品的最大愉悦度。
显然有转移方程 dp[i][j] = max(dp[j][k] + v[i])
。
因为v[i]
是定值,所以又可以写成 dp[i][j] = max(dp[j][k]) + v[i]
。
如果对于给定的i
,j
,枚举k
来找max(dp[j][k])
的话,时间复杂度会达到\(O(n^3)\)。
因此我们可以令mx[j][k]
表示购买了第j
个物品,且购买第j
个物品之前购买的一个物品在k
到j
之间的最大愉悦度。
于是转移方程就可以写成dp[i][j] = mx[j][k] + v[i]
。
如果你尝试了\(O(n^3)\)的暴力写法,你就会知道不是所有的k
都可以进行状态转移的。因为题目要求每天放入piggybank的钱的数量不能大于前一天放入的数量。所以在i
和j
确定的情况下,从第j
天到第i
天放入的钱的数量已经确定,而在第j
天放入钱数的最小值应该大于\(\frac{w[i]}{i-j}\)
即 \(\frac{w[j]}{j-k} >= \frac{w[i]}{i-j}\)
移项可以得到 \(k >= j - \frac{w[j]}{w[i]}(i-j)\) ,即为k
的最小值。
而如何维护mx[i][j]
数组呢?
可以在枚举i
的时候用已知的dp[i][j]
更新。
mx[i][j] = max(mx[i][j+1], dp[i][j]);
最后可以用max(mx[i][0])
更新答案。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 2005;
ll w[MAXN],v[MAXN];
ll dp[MAXN][MAXN],mx[MAXN][MAXN];
int main() {
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++) scanf("%lld",w+i);
for(int i = 1; i <= n; i++) scanf("%lld",v+i);
ll ans = 0;
//本题中让输入数据的下标从1开始,0空出来看作一个必买入的无价值物品,方便数组的表示。
for(int i = 1; i <= n; i++) {
dp[i][0] = v[i];
for(int j = 1; j < i; j++) {
int k = ((w[i] == 0) ? 0 : (int)max(ceil(j - 1.0*w[j]/w[i]*(i-j)),0.0));
if(mx[j][k]) dp[i][j] = mx[j][k] + v[i];
//mx[j][k] == 0 说明不往piggybank中放钱了
}
for(int j = i - 1; j >= 0; j--) {
mx[i][j] = max(mx[i][j+1], dp[i][j]);
}
}
for(int i = 1; i <= n; i++) {
ans = max(ans, mx[i][0]);
}
printf("%lld\n",ans);
return 0;
}