Money Buys Happiness§

很显然的一道动态规划题,我们发现x,和cost都蛮大的,但happiness却很小,那么我们定义状态尽量往它靠近。

所以我们定义dp[i]表示,幸福度为i下我们能得到的最多的钱。

以上定义是在我们压一维度下的体现,其实原本应该是两维,即第一维表示天数,第二维表示幸福度,但考虑到多测多次申请太多内存的开销问题,我们进行压维处理。

那么我们状态转移具体操作如下:

for (int i = 1; i < n; i++)
    for (int k = top, next; (next = k + happiness[i]) && k >= 0; k--)
        if (dp[k] >= 0 && (dp[k] += x) >= cost[i])
            dp[next] = max(dp[next], dp[k] - cost[i]), ans = max(ans, next);

由于经过作者压行处理,可读性不是很好,我们可以类比背包问题进行理解,这里作者不进行详细解释。

特别注意的是我们每一天最多买一次,那么我们需要类别01背包的模型。还有当天赚的钱当天并不能使用,这就是我进行dp[k] += x的原因,即拿到前一天的工资。

#include <bits/stdc++.h>
#define all(a) a.begin(), a.end()
#define int long long
#define PII pair<int, int>
using ll = long long;
const int N = 1e5 + 10, INF = 1e18, MOD = 1e9 + 7;
using namespace std;

void solve()
{
    int n, x;
    cin >> n >> x;
    vector<int> cost(n), happiness(n);
    for (int i = 0; i < n; i++)
        cin >> cost[i] >> happiness[i];

    int top = accumulate(all(happiness), 0ll), ans = 0;
    vector<int> dp(2e5, -1);
    dp[0] = 0;

    if (!cost[0])
        dp[ans = happiness[0]] = 0;

    for (int i = 1; i < n; i++)
        for (int k = top, next; (next = k + happiness[i]) && k >= 0; k--)
            if (dp[k] >= 0 && (dp[k] += x) >= cost[i])
                dp[next] = max(dp[next], dp[k] - cost[i]), ans = max(ans, next);

    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0);
    int T = 1;
    cin >> T, cin.get();
    while (T--)
        solve();
    return 0;
}