题解:学而思编程 杠杆原理
【题目来源】
学而思编程:杠杆原理
【题目描述】
小猴一直在利用一个杠杆翘起各种重物,杠杆左右臂的长度都是 \(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
【核心思想】
-
问题分析:给定初始右臂长度 \(L\)、初始力量 \(F\)、总资金 \(W\)。有 \(n\) 个延长杆(增加长度 \(a_i\),售价 \(b_i\))和 \(m\) 个增幅器(增加力量 \(c_j\),售价 \(d_j\))。需要选择购买一些道具(总售价不超过 \(W\)),使得力矩 \((L + sum_a) \times (F + sum_c)\) 最大。这是一个双01背包问题,关键在于将总资金分配给两个独立的背包,使得乘积最大化。
-
算法选择:
- 两个独立01背包:分别计算延长杆背包和增幅器背包
- 枚举重量分配:枚举分配给延长杆的资金,剩余给增幅器
-
关键步骤:
- 延长杆背包:
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])
- 总长度:
- 统计答案:取所有分配方案的最大力矩
- 延长杆背包:
-
时间/空间复杂度:
- 时间复杂度:\(O(n \times W + m \times W)\),两个01背包分别为 \(O(n \times W)\) 和 \(O(m \times W)\),枚举分配 \(O(W)\)
- 空间复杂度:\(O(W)\),两个一维DP数组
-
双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
浙公网安备 33010602011771号