小阅的阅读计划

[题目链接](https://www.nowcoder.com/practice/3fab21c915b74a15a591d6c8ce5735ff)

思路

本题是经典的 0/1 背包问题。有 本书,每本书有一个花费 (所需阅读积分)和价值 (收益),总共有 个阅读积分,每本书最多选一次,求能获得的最大价值。

动态规划

定义 表示恰好使用 个阅读积分时能获得的最大价值。初始时 ,其余为

对于每本书 ,从大到小枚举容量 (从 ),进行转移:

$$

为什么要倒序枚举? 因为是 0/1 背包,每本书只能选一次。如果正序枚举, 可能已经包含了当前这本书的贡献,相当于同一本书被选了多次。倒序枚举保证更新 时, 还是上一轮(不包含当前书)的状态。

最终答案为

举例

以样例 2 为例,,5 本书分别为

最优方案是选第 2、3、4 本书,花费 ,价值

复杂度

  • 时间复杂度,其中 ,完全可以接受。
  • 空间复杂度,使用一维滚动数组。

代码

#include <bits/stdc++.h>
using namespace std;
int main(){
    int s, n;
    scanf("%d%d", &s, &n);
    vector<int> dp(s + 1, 0);
    for(int i = 0; i < n; i++){
        int c, v;
        scanf("%d%d", &c, &v);
        for(int j = s; j >= c; j--){
            dp[j] = max(dp[j], dp[j - c] + v);
        }
    }
    printf("%d\n", dp[s]);
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int s = sc.nextInt();
        int n = sc.nextInt();
        int[] dp = new int[s + 1];
        for (int i = 0; i < n; i++) {
            int c = sc.nextInt();
            int v = sc.nextInt();
            for (int j = s; j >= c; j--) {
                dp[j] = Math.max(dp[j], dp[j - c] + v);
            }
        }
        System.out.println(dp[s]);
    }
}
import sys
input = sys.stdin.readline

s, n = map(int, input().split())
dp = [0] * (s + 1)
for _ in range(n):
    c, v = map(int, input().split())
    for j in range(s, c - 1, -1):
        dp[j] = max(dp[j], dp[j - c] + v)
print(dp[s])
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line.trim()));
rl.on('close', () => {
    const [s, n] = lines[0].split(' ').map(Number);
    const dp = new Array(s + 1).fill(0);
    for (let i = 0; i < n; i++) {
        const [c, v] = lines[i + 1].split(' ').map(Number);
        for (let j = s; j >= c; j--) {
            dp[j] = Math.max(dp[j], dp[j - c] + v);
        }
    }
    console.log(dp[s]);
});