【备战秋招】每日一题:塔子大厂真题模拟赛-第二题-魔法石(Ⅱ)

文章介绍了一个使用动态规划解决的问题,即在有限的能量下,如何分配以最大化魔法石点亮后的总价值。魔法石有初始、第一次点亮和第二次点亮后的三种不同价值,问题转换为分组背包问题。给出的解决方案是通过动态规划的状态转移实现,确保在不超过能量限制的情况下选取每组中的一项魔法石,以达到最大价值。代码示例分别用Python和C++展示了解决方法。
摘要由CSDN通过智能技术生成

为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接: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 1n1000,0m1000) , 分别代表魔法石的个数以及能量数量.

第二行 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 0ai,bi,ci1e9

输出描述

一行整数,代表最大价值。

样例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(nm3)=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();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值