为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1212
题目内容
塔子哥有 n n n个魔法石,初始时魔法石的价值为 a i a_i ai.塔子哥还有 m m m点能量。对于每个能量,它可以用来点亮一颗魔法石。第 i i i个魔法石第一次被点亮后的价值为 b i b_i bi,魔法石也可以被再次点亮,价值进而变为 c i c_i ci.请问塔子哥该如何安排手中的能量,使得魔法石的价值总和最大呢?
输入描述
第一行两个整数 n n n, m m m ( 1 ≤ n ≤ 1000 , 0 ≤ m ≤ 1000 1 \leq n \leq 1000 , 0 \leq m \leq 1000 1≤n≤1000,0≤m≤1000) , 分别代表魔法石的个数以及能量数量.
第二行 n n n个整数: a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an , 代表这 n n n个魔法石初始价值
第三行 n n n个整数: b 1 , b 2 , . . . , b n b_1,b_2,...,b_n b1,b2,...,bn , 代表这 n n n个魔法石第一次被点亮后的价值
第三行 n n n个整数: c 1 , c 2 , . . . , c n c_1,c_2,...,c_n c1,c2,...,cn , 代表这 n n n个魔法石第二次被点亮后的价值
0 ≤ a i , b i , c i ≤ 1 e 9 0 \leq a_i , b_i , c_i \leq 1e9 0≤ai,bi,ci≤1e9
输出描述
一行整数,代表最大价值。
样例1
输入
3 3
1 2 3
3 1 1
100 1 1
输出
105
样例2
输入
3 2
100 1 0
1 1 1
3 100 1
输出
200
思路:动态规划-分组背包
本问题等价于 有一个容积为 m m m的背包,现有 n n n组物品,每组有三个物品,第 i i i组物品为:
物品1:价值 a i a_i ai,体积 0 0 0
物品2:价值 b i b_i bi,体积 1 1 1
物品3:价值 c i c_i ci,体积 2 2 2
要求: 从这 n n n组物品中选出 n n n件物品(每组必选且只能选一件),要求容积不超过 m m m 的最大价值。这是典型的分组背包。可以自行上网学习。推荐先搞懂01背包,再学习分组背包。细节见代码
时间复杂度
有 n m nm nm个状态,3次转移,总复杂度: O ( n m ∗ 3 ) = O ( n m ) = 1 e 6 < 1 e 8 O(nm * 3) = O(nm) = 1e6 < 1e8 O(nm∗3)=O(nm)=1e6<1e8 ,可以1s过
代码
python
n , m = list(map(int , input().split()))
a = []
for i in range(3):
a.append(list(map(int , input().split())))
# 类似01背包地,分组背包也能够优化空间 , dp[i] 代表当前大小恰好为i的体积的最优解
dp = [0] * (m + 1)
# 考虑前n组物品
for i in range(n):
# <从大到小>枚举体积 -先枚举体积再枚举物品,保证组内物品互斥选择
for j in range(m , -1 , -1):
# 枚举组内物品
g = dp[j]
for k in range(2 , -1 , -1):
if j - k >= 0:
# 转移方程
g = max(g , dp[j - k] + a[k][i])
dp[j] = g
print(max(dp))
思考:如果本题 a i , b i , c i a_i,b_i,c_i ai,bi,ci 允许为负数,上述代码是否依旧正确。如果不正确,将如何修改?
C++ (from 2333)
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, m;
cin >> n >> m;
vector<int> a(n + 1), b(n + 1), c(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
for (int i = 1; i <= n; i++) {
cin >> c[i];
}
vector<ll> dp(m + 1);
for (int i = 1; i <= n; i++) {
for (int j = m; j >= 0; j--) {
dp[j] += a[i];
if (j > 0) dp[j] = max(dp[j], dp[j - 1] + b[i]);
if (j > 1) dp[j] = max(dp[j], dp[j - 2] + c[i]);
}
}
cout << *max_element(dp.begin(), dp.end()) << endl;
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t = 1;
while (t--) {
solve();
}
}