题解 | #小红笔试#
小红笔试
https://www.nowcoder.com/practice/a165da3628d6438495da2f789fb3d3c0
题目链接
题目描述
小红参加一场笔试,共有  道编程题,总时长为 
。
对于每道题,小红可以选择三种策略:
- 写正解 (A):耗时 
,得分
。
 - 写暴力 (B):耗时 
,得分
。
 - 放弃 (F):耗时 
,得分
。
 
请帮助小红制定一个策略,在总耗时不超过  的前提下,获得尽可能高的总分数。你需要输出每道题的选择('A', 'B', 或 'F')。
解题思路
这是一个典型的分组背包问题,是动态规划(DP)的经典应用之一。
1. 问题建模
我们可以将这个问题看作是一个背包问题:
- 背包容量:总时长 
。
 - 物品组:每道编程题可以看作一个“物品组”。
 - 组内物品:每个组内有三个“物品”,分别对应三种策略(A, B, F)。每个“物品”有自己的“重量”(耗时)和“价值”(得分)。
 
我们的目标是从  个物品组中,每个组至多选择一个物品放入背包,使得背包内物品的总价值(总得分)最大。
2. 动态规划设计
- 
状态定义
我们定义
dp[i][j]为:在只考虑前道题,且总用时限制为
的情况下,所能获得的最大分数。
 - 
状态转移
对于第
道题和时间限制
,我们可以从三种决策中选择一个,来更新
dp[i][j]的值:- 
放弃 (F):不对第
题进行任何操作。这种情况下,最大分数等于只考虑前
道题、在时间
内能取得的最大分数。
 - 
写暴力 (B):如果当前时间
足够(
),我们可以选择写暴力。总分将是暴力得分
加上用剩余时间
去解决前
道题所能得到的最大分数。
 - 
写正解 (A):同理,如果时间足够(
),我们可以选择写正解。
 
dp[i][j]的值就是这三者中的最大值: - 
 - 
路径记录与回溯
由于题目要求输出具体方案,我们需要在进行DP时记录下每一个状态
dp[i][j]是由哪个决策得到的。我们可以使用一个额外的path[i][j]数组来存储决策('F', 'B', 或 'A')。在填充完整个
dp和path表后,我们从path[n][t]开始,根据记录的决策逐步回溯,倒序构建出每一道题的选择,最后反转得到最终答案。 
代码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
struct Problem {
    int ta, sa, tb, sb;
};
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int n, t;
    cin >> n >> t;
    vector<Problem> problems(n);
    for (int i = 0; i < n; ++i) {
        cin >> problems[i].ta >> problems[i].sa >> problems[i].tb >> problems[i].sb;
    }
    vector<vector<int>> dp(n + 1, vector<int>(t + 1, 0));
    vector<vector<char>> path(n + 1, vector<char>(t + 1, 'F'));
    for (int i = 1; i <= n; ++i) {
        Problem p = problems[i - 1];
        for (int j = 0; j <= t; ++j) {
            // Default: Fail
            dp[i][j] = dp[i - 1][j];
            path[i][j] = 'F';
            // Try Brute-force
            if (j >= p.tb && dp[i - 1][j - p.tb] + p.sb > dp[i][j]) {
                dp[i][j] = dp[i - 1][j - p.tb] + p.sb;
                path[i][j] = 'B';
            }
            // Try AC
            if (j >= p.ta && dp[i - 1][j - p.ta] + p.sa > dp[i][j]) {
                dp[i][j] = dp[i - 1][j - p.ta] + p.sa;
                path[i][j] = 'A';
            }
        }
    }
    string result = "";
    int current_t = t;
    for (int i = n; i >= 1; --i) {
        char choice = path[i][current_t];
        result += choice;
        if (choice == 'A') {
            current_t -= problems[i - 1].ta;
        } else if (choice == 'B') {
            current_t -= problems[i - 1].tb;
        }
    }
    reverse(result.begin(), result.end());
    cout << result << endl;
    return 0;
}
import java.util.Scanner;
class Problem {
    int ta, sa, tb, sb;
    public Problem(int ta, int sa, int tb, int sb) {
        this.ta = ta;
        this.sa = sa;
        this.tb = tb;
        this.sb = sb;
    }
}
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int t = sc.nextInt();
        Problem[] problems = new Problem[n];
        for (int i = 0; i < n; i++) {
            problems[i] = new Problem(sc.nextInt(), sc.nextInt(), sc.nextInt(), sc.nextInt());
        }
        int[][] dp = new int[n + 1][t + 1];
        char[][] path = new char[n + 1][t + 1];
        for (int i = 1; i <= n; i++) {
            Problem p = problems[i - 1];
            for (int j = 0; j <= t; j++) {
                // Default: Fail
                dp[i][j] = dp[i - 1][j];
                path[i][j] = 'F';
                // Try Brute-force
                if (j >= p.tb && dp[i - 1][j - p.tb] + p.sb > dp[i][j]) {
                    dp[i][j] = dp[i - 1][j - p.tb] + p.sb;
                    path[i][j] = 'B';
                }
                // Try AC
                if (j >= p.ta && dp[i - 1][j - p.ta] + p.sa > dp[i][j]) {
                    dp[i][j] = dp[i - 1][j - p.ta] + p.sa;
                    path[i][j] = 'A';
                }
            }
        }
        StringBuilder result = new StringBuilder();
        int currentT = t;
        for (int i = n; i >= 1; i--) {
            char choice = path[i][currentT];
            result.append(choice);
            if (choice == 'A') {
                currentT -= problems[i - 1].ta;
            } else if (choice == 'B') {
                currentT -= problems[i - 1].tb;
            }
        }
        System.out.println(result.reverse().toString());
    }
}
import sys
def solve():
    try:
        n, t = map(int, sys.stdin.readline().split())
        problems = []
        for _ in range(n):
            problems.append(list(map(int, sys.stdin.readline().split())))
    except (IOError, ValueError):
        return
    dp = [[0] * (t + 1) for _ in range(n + 1)]
    path = [['F'] * (t + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        ta, sa, tb, sb = problems[i-1]
        for j in range(t + 1):
            # Default: Fail
            dp[i][j] = dp[i-1][j]
            path[i][j] = 'F'
            # Try Brute-force
            if j >= tb and dp[i-1][j-tb] + sb > dp[i][j]:
                dp[i][j] = dp[i-1][j-tb] + sb
                path[i][j] = 'B'
            # Try AC
            if j >= ta and dp[i-1][j-ta] + sa > dp[i][j]:
                dp[i][j] = dp[i-1][j-ta] + sa
                path[i][j] = 'A'
    result = []
    current_t = t
    for i in range(n, 0, -1):
        choice = path[i][current_t]
        result.append(choice)
        if choice == 'A':
            current_t -= problems[i-1][0]
        elif choice == 'B':
            current_t -= problems[i-1][2]
    
    print("".join(reversed(result)))
solve()
算法及复杂度
- 
算法:动态规划 (分组背包)
 - 
时间复杂度:
。
- 我们需要填充一个大小为 
的DP表,每个状态的计算是常数时间。
 
 - 我们需要填充一个大小为 
 - 
空间复杂度:
。
- 我们需要 
的空间来存储
dp表和path表。 
 - 我们需要 
 

上海得物信息集团有限公司公司福利 1174人发布