1 条题解

  • 0
    @ 2026-10-27 1:47:48

    思路与算法

    工作i先付D_i再收回C_i,总资金减少D_i-C_i>0。按C_i从大到小排序;dp[b]表示剩余b元最多已做多少项,初始仅dp[M]=0。每项按b从小到大扫描,若b≥D_i,用dp[b]+1更新dp[b-D_i+C_i]。最后取最大值。

    正确性说明

    若C_i≥C_j,先做j再做i所需资金为max(D_j,D_j-C_j+D_i),它既不小于D_i,也不小于D_i-C_i+D_j,故交换成i先j后不增加初始资金要求。任何可行子集因此能按此顺序完成。DP逐项包含选与不选两类;目标资金严格更小且已经扫描过,所以本项不会重复使用。按项归纳得每种剩余资金的最优项数,取最大即全局答案。

    复杂度

    O(N log N+NM)时间、O(N+M)空间,包含作业排序和M+1个状态初始化。

    易错点

    收益C_i不是净收入,资金会严格减少;必须付得起D_i。这里扫描剩余资金升序,不能套用另一种容量定义的倒序模板。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int n, M;
        cin >> n >> M;
        vector<pair<int, int>> a(n);
        for (auto &x : a)
            cin >> x.second;
        for (auto &x : a)
            cin >> x.first;
        sort(a.rbegin(), a.rend());
        vector<int> d(M + 1, -1000000);
        d[M] = 0;
        for (auto x : a) {
            int C = x.first, D = x.second, loss = D - C;
            for (int money = D; money <= M; money++)
                if (d[money] >= 0)
                    d[money - loss] = max(d[money - loss], d[money] + 1);
        }
        cout << *max_element(d.begin(), d.end()) << "\n";
    }
    
    • 1

    信息

    ID
    1968
    时间
    1000ms
    内存
    128MiB
    难度
    普及
    标签
    递交数
    0
    已通过
    0
    上传者