【动态规划】 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]
如果对于给定的ij,枚举k来找max(dp[j][k])的话,时间复杂度会达到\(O(n^3)\)
因此我们可以令mx[j][k]表示购买了第j个物品,且购买第j个物品之前购买的一个物品在kj之间的最大愉悦度。
于是转移方程就可以写成dp[i][j] = mx[j][k] + v[i]

如果你尝试了\(O(n^3)\)的暴力写法,你就会知道不是所有的k都可以进行状态转移的。因为题目要求每天放入piggybank的钱的数量不能大于前一天放入的数量。所以在ij确定的情况下,从第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;
}
posted @ 2019-05-04 20:47  NeilThang  阅读(505)  评论(0编辑  收藏  举报