今天我们将在豆包MarsCode AI刷题平台上,完成《小F的超市购物策略》与《环形数组最大子数组和的问题》这两个算法问题,通过这些练习提升用户解决此类问题的能力
《小F的超市购物策略》题面如下:
问题理解
题目要求在有限的预算 x 内,通过购买商品并利用超市的半价活动,最大化喜爱度总和。每个商品的价格是偶数,购买某件商品时,可以选择以原价购买,或者以半价购买下一件商品。
数据结构选择
使用了三维动态规划数组 f,其中:
-
f[i][j][k]表示在前i个商品中,状态为j时,花费k元所能获得的最大喜爱度。 -
i表示当前商品的索引。 -
j表示当前的状态:0:当前商品以半价购买。1:当前商品以原价购买。2:当前商品不购买。
-
k表示当前花费的金额。
算法步骤
-
初始化:
- 对于第一个商品,如果其价格
a[0]小于等于预算x,则可以以原价购买,状态为1,花费为a[0],喜爱度为b[0]。
- 对于第一个商品,如果其价格
-
状态转移:
-
对于每个商品
i和每个可能的花费j:- 状态
0:如果前一个商品以原价购买(状态1),并且当前商品可以以半价购买(即j - a[i]/2 >= 0),则更新当前状态为0。 - 状态
1:如果当前商品可以以原价购买(即j - a[i] >= 0),则从前一个商品的任意状态(0,1,2)转移过来,更新当前状态为1。 - 状态
2:不购买当前商品,直接从前一个商品的任意状态(0,1,2)转移过来。
- 状态
-
-
结果计算:
- 遍历所有商品和所有可能的花费,取最大喜爱度作为结果。
#include <bits/stdc++.h>
using namespace std;
int solution(int n, int x, std::vector<int> a, std::vector<int> b) {
int ret = 0;
vector<vector<vector<int>>> f(n, vector<vector<int>>(3, vector<int>(x + 1, 0)));
// 初始化第一个商品
if (a[0] <= x) {
f[0][1][a[0]] = b[0];
ret = b[0];
}
// 状态转移
for (int i = 1; i < n; i++) {
for (int j = 0; j <= x; j++) {
// 状态 0:当前商品以半价购买
if (j - a[i] / 2 >= 0 && f[i - 1][1][j - a[i] / 2] > 0) {
f[i][0][j] = f[i - 1][1][j - a[i] / 2] + b[i];
}
// 状态 1:当前商品以原价购买
if (j - a[i] >= 0) {
f[i][1][j] = max({f[i - 1][0][j - a[i]] + b[i],
f[i - 1][1][j - a[i]] + b[i], f[i - 1][2][j - a[i]] + b[i]});
}
// 状态 2:当前商品不购买
f[i][2][j] = max({f[i - 1][2][j], f[i - 1][0][j], f[i - 1][1][j]});
// 更新结果
ret = max({ret, f[i][0][j], f[i][1][j], f[i][2][j]});
}
}
return ret;
}
int main() {
// 测试样例
std::cout << (solution(4, 7, {2, 2, 6, 2}, {3, 4, 5, 1}) == 12) << std::endl;
std::cout << (solution(3, 10, {4, 4, 4}, {2, 3, 5}) == 10) << std::endl;
std::cout << (solution(5, 8, {2, 4, 4, 6, 2}, {1, 2, 3, 4, 5}) == 10) << std::endl;
std::cout << (solution(13, 7, {28, 12, 10, 16, 12, 34, 32, 8, 12, 30, 12, 28, 22}, {26, 30, 12, 32, 14, 22, 20, 18, 14, 8, 6, 6, 2}) == 0) << std::endl;
std::cout << (solution(17, 14, {32, 18, 32, 12, 10, 2, 12, 28, 22, 2, 18, 22, 16, 4, 4, 32, 2}, {16, 32, 4, 6, 24, 22, 32, 28, 32, 32, 30, 2, 34, 24, 16, 32, 6}) == 110) << std::endl;
}
Tip: 目前的实现可以继续使用滚动数组优化,感兴趣的朋友可以自己试一试
《环形数组最大子数组和的问题》题面如下:
题目理解
题目要求在一个环形数组中找到非空子数组的最大可能和。环形数组的特点是数组的末端和开头相连,这意味着子数组可以跨越数组的末端连接到开头。
解题思路
为了解决这个问题,我们可以将问题分解为两个部分:
- 普通子数组的最大和:即在非环形数组中找到的最大子数组和。
- 环形子数组的最大和:即跨越数组末端和开头的最大子数组和。
普通子数组的最大和
在遍历数组时维护两个变量:
preSum:开头位置到当前位置子数组的和。preMinSum:前缀子数组的最小和。
以当前元素为终点的普通子数组的最大和等于preSum - preMinSum
环形子数组的最大和
环形子数组的最大和可以通过以下方式计算:
- 计算整个数组的和
allSum。 preSum:开头位置到当前位置子数组的和。preMaxSum:后缀子数组的最大和。
以当前元素为起点的环形子数组的最大和为allSum - preSum + preMaxSum
具体实现
int solution(std::vector<int>& nums) {
// write code here
int n = nums.size();
int ret = INT_MIN;
int allSum=0;
for(int i : nums){
allSum += i;
}
int preMinSum=0, preMaxSum=0, preSum=0;
for(int i=0;i<n;i++){
// 计算环形子数组的最大和
ret = max(ret, allSum - preSum + preMaxSum);
preSum += nums[i];
// 计算普通子数组的最大和
ret = max(ret, preSum - preMinSum);
preMinSum = min(preMinSum, preSum);
preMaxSum = max(preMaxSum, preSum);
}
return ret; // placeholder return
}
总体复杂度
-
时间复杂度:
O(n),其中n是数组的长度。 -
空间复杂度:
O(1)
借助豆包MarsCode AI刷题平台,我们不仅高效地解决了《小F的超市购物策略》和《环形数组最大子数组和的问题》,还加深了对相关算法和数据结构的理解,后续会借助豆包MarsCode AI给大家展示更多题目的解法。