小红笔试

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

思路

本题是一个经典的分组背包问题。每道题有三种选择:写正解(A)、写暴力(B)、放弃(F),需要在总时间 内最大化总得分,并输出具体方案。

建模

将总时间视为背包容量,每道题视为一组物品:

  • A:耗时 ,得分
  • B:耗时 ,得分
  • F:耗时 ,得分

每组恰好选一个,这就是标准的分组背包。

DP 定义与转移

定义 为前 道题、耗时恰好为 时的最大得分。转移方程:

$$

方案回溯

在 DP 过程中,记录每个状态 的最优选择( 表示 F, 表示 A, 表示 B)。最终从 出发,按 逆序回溯,还原完整的策略字符串。

空间优化

对于 C++/Java/JS,可以使用二维数组直接存储。对于 Python,为了避免 TLE,使用滚动数组:每一轮复制上一轮的 数组,然后在副本上更新 A 和 B 的选择,避免使用二维列表的嵌套开销。

复杂度分析

  • 时间复杂度,每道题对每个容量做常数次比较。
  • 空间复杂度,存储 DP 数组和选择数组。使用滚动数组可将 DP 数组降为 ,但选择数组仍需

代码

#include <bits/stdc++.h>
using namespace std;

int main(){
    int n, T;
    scanf("%d%d", &n, &T);
    vector<int> ta(n), sa(n), tb(n), sb(n);
    for(int i = 0; i < n; i++){
        scanf("%d%d%d%d", &ta[i], &sa[i], &tb[i], &sb[i]);
    }

    vector<vector<int>> dp(n+1, vector<int>(T+1, 0));
    vector<vector<int>> choice(n, vector<int>(T+1, 0));

    for(int i = 0; i < n; i++){
        for(int j = 0; j <= T; j++){
            dp[i+1][j] = dp[i][j];
            choice[i][j] = 0;
            if(j >= ta[i] && dp[i][j - ta[i]] + sa[i] > dp[i+1][j]){
                dp[i+1][j] = dp[i][j - ta[i]] + sa[i];
                choice[i][j] = 1;
            }
            if(j >= tb[i] && dp[i][j - tb[i]] + sb[i] > dp[i+1][j]){
                dp[i+1][j] = dp[i][j - tb[i]] + sb[i];
                choice[i][j] = 2;
            }
        }
    }

    string ans(n, 'F');
    int j = T;
    for(int i = n-1; i >= 0; i--){
        if(choice[i][j] == 1){
            ans[i] = 'A';
            j -= ta[i];
        } else if(choice[i][j] == 2){
            ans[i] = 'B';
            j -= tb[i];
        }
    }

    printf("%s\n", ans.c_str());
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), T = sc.nextInt();
        int[] ta = new int[n], sa = new int[n], tb = new int[n], sb = new int[n];
        for (int i = 0; i < n; i++) {
            ta[i] = sc.nextInt();
            sa[i] = sc.nextInt();
            tb[i] = sc.nextInt();
            sb[i] = sc.nextInt();
        }

        int[][] dp = new int[n + 1][T + 1];
        int[][] choice = new int[n][T + 1];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= T; j++) {
                dp[i + 1][j] = dp[i][j];
                choice[i][j] = 0;
                if (j >= ta[i] && dp[i][j - ta[i]] + sa[i] > dp[i + 1][j]) {
                    dp[i + 1][j] = dp[i][j - ta[i]] + sa[i];
                    choice[i][j] = 1;
                }
                if (j >= tb[i] && dp[i][j - tb[i]] + sb[i] > dp[i + 1][j]) {
                    dp[i + 1][j] = dp[i][j - tb[i]] + sb[i];
                    choice[i][j] = 2;
                }
            }
        }

        StringBuilder ans = new StringBuilder();
        for (int i = 0; i < n; i++) ans.append('F');
        int j = T;
        for (int i = n - 1; i >= 0; i--) {
            if (choice[i][j] == 1) {
                ans.setCharAt(i, 'A');
                j -= ta[i];
            } else if (choice[i][j] == 2) {
                ans.setCharAt(i, 'B');
                j -= tb[i];
            }
        }

        System.out.println(ans.toString());
    }
}
import sys

def main():
    data = sys.stdin.buffer.read().split()
    idx = 0
    n = int(data[idx]); idx += 1
    T = int(data[idx]); idx += 1
    ta = [0]*n; sa = [0]*n; tb = [0]*n; sb = [0]*n
    for i in range(n):
        ta[i] = int(data[idx]); idx += 1
        sa[i] = int(data[idx]); idx += 1
        tb[i] = int(data[idx]); idx += 1
        sb[i] = int(data[idx]); idx += 1

    dp = [0] * (T + 1)
    choice = [bytearray(T + 1) for _ in range(n)]

    for i in range(n):
        tai = ta[i]; sai = sa[i]; tbi = tb[i]; sbi = sb[i]
        ci = choice[i]
        new_dp = dp[:]
        for j in range(T + 1):
            if j >= tai:
                v = dp[j - tai] + sai
                if v > new_dp[j]:
                    new_dp[j] = v
                    ci[j] = 1
            if j >= tbi:
                v = dp[j - tbi] + sbi
                if v > new_dp[j]:
                    new_dp[j] = v
                    ci[j] = 2
        dp = new_dp

    ans = ['F'] * n
    j = T
    for i in range(n - 1, -1, -1):
        c = choice[i][j]
        if c == 1:
            ans[i] = 'A'
            j -= ta[i]
        elif c == 2:
            ans[i] = 'B'
            j -= tb[i]

    sys.stdout.write(''.join(ans) + '\n')

main()
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 [n, T] = lines[0].split(' ').map(Number);
    const ta = [], sa = [], tb = [], sb = [];
    for (let i = 0; i < n; i++) {
        const parts = lines[i + 1].split(' ').map(Number);
        ta.push(parts[0]); sa.push(parts[1]);
        tb.push(parts[2]); sb.push(parts[3]);
    }

    const dp = Array.from({length: n + 1}, () => new Int32Array(T + 1));
    const choice = Array.from({length: n}, () => new Int8Array(T + 1));

    for (let i = 0; i < n; i++) {
        for (let j = 0; j <= T; j++) {
            dp[i + 1][j] = dp[i][j];
            choice[i][j] = 0;
            if (j >= ta[i] && dp[i][j - ta[i]] + sa[i] > dp[i + 1][j]) {
                dp[i + 1][j] = dp[i][j - ta[i]] + sa[i];
                choice[i][j] = 1;
            }
            if (j >= tb[i] && dp[i][j - tb[i]] + sb[i] > dp[i + 1][j]) {
                dp[i + 1][j] = dp[i][j - tb[i]] + sb[i];
                choice[i][j] = 2;
            }
        }
    }

    const ans = new Array(n).fill('F');
    let j = T;
    for (let i = n - 1; i >= 0; i--) {
        if (choice[i][j] === 1) {
            ans[i] = 'A';
            j -= ta[i];
        } else if (choice[i][j] === 2) {
            ans[i] = 'B';
            j -= tb[i];
        }
    }

    console.log(ans.join(''));
});