P2392 kkksc03考前临时抱佛脚——暴力枚举/动态规划

题目背景

kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。

题目描述

这次期末考试,kkksc03 需要考 \(4\) 科。因此要开始刷习题集,每科都有一个习题集,分别有 \(s_1,s_2,s_3,s_4\) 道题目,完成每道题目需要一些时间,可能不等(\(A_1,A_2,\ldots,A_{s_1}\)\(B_1,B_2,\ldots,B_{s_2}\)\(C_1,C_2,\ldots,C_{s_3}\)\(D_1,D_2,\ldots,D_{s_4}\))。

kkksc03 有一个能力,他的左右两个大脑可以同时计算 \(2\) 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。

由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。

输入格式

本题包含 \(5\) 行数据:第 \(1\) 行,为四个正整数 \(s_1,s_2,s_3,s_4\)

\(2\) 行,为 \(A_1,A_2,\ldots,A_{s_1}\)\(s_1\) 个数,表示第一科习题集每道题目所消耗的时间。

\(3\) 行,为 \(B_1,B_2,\ldots,B_{s_2}\)\(s_2\) 个数。

\(4\) 行,为 \(C_1,C_2,\ldots,C_{s_3}\)\(s_3\) 个数。

\(5\) 行,为 \(D_1,D_2,\ldots,D_{s_4}\)\(s_4\) 个数,意思均同上。

输出格式

输出一行,为复习完毕最短时间。

输入输出样例 #1

输入 #1

1 2 1 3		
5
4 3
6
2 4 3

输出 #1

20

说明/提示

\(1\leq s_1,s_2,s_3,s_4\leq 20\)

\(1\leq A_1,A_2,\ldots,A_{s_1},B_1,B_2,\ldots,B_{s_2},C_1,C_2,\ldots,C_{s_3},D_1,D_2,\ldots,D_{s_4}\leq60\)

题解

暴力枚举法

暴力枚举法的核心思想是对每科的题目所有可能的分配方式进行枚举,找出左右脑完成该科复习的最短时间。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 计算一科的最短复习时间
int bruteForce(vector<int>& problems) {
    int n = problems.size();
    int minTime = 1e9;
    // 枚举所有可能的分配方式
    for (int mask = 0; mask < (1 << n); ++mask) {
        int leftTime = 0, rightTime = 0;
        for (int i = 0; i < n; ++i) {
            if (mask & (1 << i)) {
                leftTime += problems[i];
            } else {
                rightTime += problems[i];
            }
        }
        // 更新最短时间
        minTime = min(minTime, max(leftTime, rightTime));
    }
    return minTime;
}

int main() {
    vector<int> s(4);
    for (int i = 0; i < 4; ++i) {
        cin >> s[i];
    }

    int totalTime = 0;
    for (int i = 0; i < 4; ++i) {
        vector<int> problems(s[i]);
        for (int j = 0; j < s[i]; ++j) {
            cin >> problems[j];
        }
        // 计算一科的最短复习时间并累加
        totalTime += bruteForce(problems);
    }

    cout << totalTime << endl;
    return 0;
}

动态规划法

动态规划法将问题转化为 0 - 1 背包问题,通过状态转移方程找出在背包容量为总时间一半的情况下能装入的最大题目时间总和。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 动态规划计算一科的最短复习时间
int dp(vector<int>& problems) {
    int sumTime = 0;
    for (int time : problems) {
        sumTime += time;
    }
    int capacity = sumTime / 2;
    vector<int> dp(capacity + 1, 0);
    for (int time : problems) {
        for (int j = capacity; j >= time; --j) {
            dp[j] = max(dp[j], dp[j - time] + time);
        }
    }
    return max(dp[capacity], sumTime - dp[capacity]);
}

int main() {
    vector<int> s(4);
    for (int i = 0; i < 4; ++i) {
        cin >> s[i];
    }

    int totalTime = 0;
    for (int i = 0; i < 4; ++i) {
        vector<int> problems(s[i]);
        for (int j = 0; j < s[i]; ++j) {
            cin >> problems[j];
        }
        // 计算一科的最短复习时间并累加
        totalTime += dp(problems);
    }

    cout << totalTime << endl;
    return 0;
}
posted @ 2025-02-22 12:43  ToFuture$  阅读(55)  评论(0)    收藏  举报