一、翻转增益的最大子数组和
问题描述
小C面对一个由整数构成的数组,他考虑通过一次操作提升数组的潜力。这个操作允许他选择数组中的任一子数组并将其翻转,目的是在翻转后的数组中找到具有最大和的子数组。小C对这个可能性很感兴趣,并希望知道翻转后的数组中可能得到的最大子数组和是多少。
例如,数组是 1, 2, 3, -1, 4。小C可以选择翻转子数组 -1, 4 得到 1, 2, 3, 4, -1 或者翻转 1, 2, 3, -1 得到 -1, 3, 2, 1, 4,在这两种情况下,最大的子数组和都是 10。
备注:子数组 是数组中的一个连续部分。
输入
- N: 数组的长度
- data_array: 一个长度为 N 的整数数组
输出
请你返回执行翻转操作后(也可以选择不翻转),数组中可能获得的最大子数组和。
测试样例
样例1:
输入:
N = 5,data_array = [1, 2, 3, -1, 4]
输出:10
样例2:
输入:
N = 4,data_array = [-3, -1, -2, 3]
输出:3
样例3:
输入:
N = 3,data_array = [-1, -2, -3]
输出:-1
样例4:
输入:
N = 6,data_array = [-5, -9, 6, 7, -6, 2]
输出:15
样例5:
输入:
N = 7,data_array = [-8, -1, -2, -3, 4, -5, 6]
输出:10
解题思路:
问题理解
我们需要找到在翻转任意子数组后,数组中可能获得的最大子数组和。子数组是数组中的一个连续部分。
关键点
- 翻转操作:我们可以选择任意子数组进行翻转。
- 最大子数组和:在翻转后的数组中,我们需要找到具有最大和的子数组。
解题思路
- 不翻转的情况:首先,我们可以计算原始数组的最大子数组和,这可以通过Kadane算法来实现。
- 翻转的情况:考虑翻转子数组的影响。翻转一个子数组可能会改变数组的局部结构,从而影响最大子数组和。
具体步骤
-
计算原始数组的最大子数组和:使用Kadane算法。
-
考虑翻转的影响:
- 翻转一个子数组后,新的最大子数组和可能出现在翻转后的子数组内部,或者跨越翻转的边界。
- 对于每个可能的翻转子数组,计算翻转后的最大子数组和。
-
综合考虑:比较不翻转的最大子数组和与翻转后的最大子数组和,取最大值。
最终代码:
#include <bits/stdc++.h>
using namespace std;
int max_subarray_sum(const vector<int>& arr) {
int max_ending_here = arr[0];
int max_so_far = arr[0];
for(int i = 1;i<arr.size();i++){
max_ending_here = max(arr[i],arr[i]+max_ending_here);
max_so_far = max(max_ending_here,max_so_far);
}
}
int solution(int N, vector<int>& data_array) {
// 计算整个数组的和
int max_sum_without_flip = max_subarray_sum(data_array);
// 计算考虑翻转的最大子数组和
int max_sum_with_flip = max_sum_without_flip;
for (int start = 0; start < N; ++start) {
for (int end = start; end < N; ++end) {
// 创建一个新数组,其中子数组从头到尾颠倒
vector<int> flipped_array = data_array;
reverse(flipped_array.begin() + start, flipped_array.begin() + end + 1);
// 计算翻转后的和
int current_max_sum = max_subarray_sum(flipped_array);
// 比较得出最大值
max_sum_with_flip = max(max_sum_with_flip, current_max_sum);
}
}
return max(max_sum_without_flip, max_sum_with_flip);
}
int main() {
// Test cases
vector<int> array1 = {1, 2, 3, -1, 4};
vector<int> array2 = {-3, -1, -2, 3};
vector<int> array3 = {-1, -2, -3};
vector<int> array4 = {-5, -9, 6, 7, -6, 2};
vector<int> array5 = {-8, -1, -2, -3, 4, -5, 6};
cout << (solution(5, array1) == 10) << endl; // Expected output: 1
cout << (solution(4, array2) == 3) << endl; // Expected output: 1
cout << (solution(3, array3) == -1) << endl; // Expected output: 1
cout << (solution(6, array4) == 15) << endl; // Expected output: 1
cout << (solution(7, array5) == 10) << endl; // Expected output: 1
return 0;
}
运行结果:
二、奇妙货币交易问题
问题描述
小R住在一个名为 X 国的国家,这里的货币非常特殊,面值为 V0,V1,V2,...,VnV0,V1,V2,...,Vn,并且 nn 可以无限大。该国的交易规则也很特别:在一次交易中,双方只能对每种面值的货币使用不超过两次。
例如,小R想买一件价格为 198 的物品,货币的基数 V=10V=10 时,小R可以使用 2 张 100(102)100(102) 的纸币,卖家则找回 2 张 1(100)1(100) 的纸币。由于这个奇怪的规则,很多 X 国人都无法快速判断某个物品是否可以用这种方式交易成功,他们常常会请聪明的你来帮助。
你能帮他们判断一下,是否能按照规则用给定的货币面值 VV 来完成价格为 WW 的交易吗?
测试样例
样例1:
输入:
V = 10,W = 9
输出:'YES'
样例2:
输入:
V = 200,W = 40199
输出:'YES'
样例3:
输入:
V = 108,W = 50
输出:'NO'
解题思路:
-
问题理解:
- 你需要判断是否可以通过使用每种面值的货币不超过两次来完成交易。这里的货币面值是 V0,V1,V2,...,即 1,V,V2,V3,...。
-
核心思路:
- 通过不断取模和除法操作,判断是否可以在每一步中将
W分解为不超过两次的每种面值的货币。
- 通过不断取模和除法操作,判断是否可以在每一步中将
-
具体步骤:
-
取模操作:
int remainder = W % V;计算W除以V的余数。 -
条件判断:
- 如果
remainder > 2,说明当前的余数无法用不超过两次的每种面值的货币凑出,因此尝试调整remainder并更新W。 - 如果
remainder < -2,直接返回"NO",因为负数的余数也无法用不超过两次的每种面值的货币凑出。 - 如果
remainder在 -2 到 2 之间,直接更新W。
- 如果
-
最终代码:
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
using namespace std;
std::string solution(int V, int W) {
if(V==1)
return "YES";
while (W != 0) {
int remainder = W % V;
if (remainder > 2) {
remainder -= V;
W = (W - remainder) / V;
} else if (remainder < -2) {
return "NO";
} else {
W = W / V;
}
if (abs(remainder) > 2) {
return "NO";
}
}
return "YES";
}
int main() {
std::cout << (solution(10, 9) == "YES") << std::endl;
std::cout << (solution(200, 40199) == "YES") << std::endl;
std::cout << (solution(108, 50) == "NO") << std::endl;
return 0;
}
运行结果: