题解:学而思编程 杠杆原理

【题目来源】

学而思编程:杠杆原理

【题目描述】

小猴一直在利用一个杠杆翘起各种重物,杠杆左右臂的长度都是 \(L\),小猴在右臂施加的力量为 \(F\),根据杠杆原理右边的力矩为 \(L×F\)。力矩越大,就能翘起越重的东西。现在小猴想要购买强化道具来改造这个杠杆,使得它能翘起更重的东西。

可以用于改造杠杆的强化道具有两种:延长杆可以增加右臂的长度;增幅器可以增加在右臂施加的力量。

\(n\) 个延长杆,第 \(i\) 个延长杆可以将右臂长度增加 \(a_i\),售价 \(b_i\) 元。有 \(m\) 个增幅器,第 \(j\) 个增幅器可以将力量增加 \(c_j\),售价 \(d_j\) 元。

小猴一共只有 \(W\) 元,他要从这些强化道具中选择一些购买(总售价不超过 \(W\)),使得改造后的力矩最大。

力矩的计算方式:设购买到的延长杆总长度为 \(sum_a\),增幅器总力量为 \(sum_c\),那么力矩等于 \((L+sum_a)×(F+sum_c)\),小猴想要让这个式子值最大。

【输入】

\(1\) 行,\(3\) 个整数 \(L,F,W\),分别表示初始右臂长度、初始力量、小猴的资金。

\(2\) 行,\(1\) 个整数 \(n\),表示延长杆的个数。

接下来 \(n\) 行,每行两个正整数 \(a_i,b_i\)、表示第 \(i\) 个延长杆的长度和售价。

\(n+3\) 行,\(1\) 个整数 \(m\),表示增幅器的个数。

接下来 \(m\) 行,每行两个正整数 \(c_j,d_j\)、表示第 \(j\) 个增幅器的力量和售价。

【输出】

\(1\) 个正整数,可以得到的最大力矩。

【输入样例】

10 3 39
3
7 2
1 3
10 10
3
5 13
5 10
6 10

【输出样例】

392

【核心思想】

  1. 问题分析:给定初始右臂长度 \(L\)、初始力量 \(F\)、总资金 \(W\)。有 \(n\) 个延长杆(增加长度 \(a_i\),售价 \(b_i\))和 \(m\) 个增幅器(增加力量 \(c_j\),售价 \(d_j\))。需要选择购买一些道具(总售价不超过 \(W\)),使得力矩 \((L + sum_a) \times (F + sum_c)\) 最大。这是一个双01背包问题,关键在于将总资金分配给两个独立的背包,使得乘积最大化。

  2. 算法选择

    • 两个独立01背包:分别计算延长杆背包和增幅器背包
    • 枚举重量分配:枚举分配给延长杆的资金,剩余给增幅器
  3. 关键步骤

    • 延长杆背包f[j] 表示花费不超过 \(j\) 时的最大长度增加量
    • 增幅器背包g[j] 表示花费不超过 \(j\) 时的最大力量增加量
    • 01背包转移f[j] = max(f[j], f[j - b[i]] + a[i])g[j] = max(g[j], g[j - d[i]] + c[i])
    • 枚举分配:对于 \(i\)\(0\)\(W\)
      • 总长度:L + f[i]
      • 总力量:F + g[W - i]
      • 力矩:(L + f[i]) \times (F + g[W - i])
    • 统计答案:取所有分配方案的最大力矩
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times W + m \times W)\),两个01背包分别为 \(O(n \times W)\)\(O(m \times W)\),枚举分配 \(O(W)\)
    • 空间复杂度:\(O(W)\),两个一维DP数组
  5. 双01背包(重量分配求乘积最大)的核心思想

    • 问题拆分:将双背包问题拆分为两个独立的01背包问题
    • 分别预处理:分别计算两个背包在每个资金限制下的最优值
    • 枚举分配策略:枚举分配给第一个背包的资金,剩余自动分配给第二个背包
    • 乘积最大化:目标是最大化两个维度的乘积,而非简单的和
    • 适用于"两个维度乘积最大化"的背包问题

【算法标签】

01背包

【代码详解】

#include<iostream>
using namespace std;

int L, F, W, n, m, a[5005], b[5005], c[5005], d[5005];
long long f[5005], g[5005];  // f: 第一个背包的DP数组, g: 第二个背包的DP数组

int main()
{
    cin >> L >> F >> W;  // 初始攻击力, 初始防御力, 总重量上限
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> b[i];  // 攻击力加成, 重量
    }
    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> c[i] >> d[i];  // 防御力加成, 重量
    }

    // 0/1背包:计算攻击力物品的最大攻击力加成
    for (int i = 1; i <= n; i++)
    {
        for (int j = W; j >= b[i]; j--)
        {
            f[j] = max(f[j], f[j - b[i]] + a[i]);
        }
    }

    // 0/1背包:计算防御力物品的最大防御力加成
    for (int i = 1; i <= m; i++)
    {
        for (int j = W; j >= d[i]; j--)
        {
            g[j] = max(g[j], g[j - d[i]] + c[i]);
        }
    }

    long long ans = 0;
    // 枚举分配给攻击力物品的重量i,剩余重量W-i给防御力物品
    for (int i = 0; i <= W; i++)
    {
        // 总攻击力 = 初始攻击力 + 攻击力加成
        // 总防御力 = 初始防御力 + 防御力加成
        ans = max(ans, (L + f[i]) * (F + g[W - i]));
    }
    cout << ans << endl;

    return 0;
}

【运行结果】

10 3 39
3
7 2
1 3
10 10
3
5 13
5 10
6 10
392
posted @ 2026-06-14 10:47  团爸讲算法  阅读(8)  评论(0)    收藏  举报