9.21 快选|倍增|栈+贡献法
lc2104
单调栈+贡献法 借鉴lc907
单调栈分别计算数组所有子数组的最大值之和与最小值之和
两者相加(因最小值通过取负转为求最大值)得到所有子数组的极差之和
lc907图解
class Solution {
public:
// 求nums中子数组所有最大值之和
long long solver(vector<int>& nums)
{
// 考虑每个元素作为最大值出现在了多少子数组中
// 求出nums[i]左侧严格大于他的最近元素left[i]
// 和右侧严格大于等于它的最近元素right[i]
// 因为nums中可能右重复元素,所以这里右侧取大于等于,
int n = nums.size();
vector<int> left(n, -1), right(n, n);
stack<int> st;
for (int i = 0; i < n; i++) {
while (!st.empty() && nums[st.top()] <= nums[i]) {
right[st.top()] = i; // i 恰好是栈顶元素的右边界
st.pop();
}
if (!st.empty()) {
left[i] = st.top();
}
st.push(i);
}
long long ans = 0;
for (int i = 0; i < n; i++) {
ans += (long long)nums[i] * (i - left[i]) * (right[i] - i);
}
return ans;
}
long long subArrayRanges(vector<int>& nums) {
// 最大元素和最小元素的差值
// 等价于 先求最大元素之和,在求数组元素*(-1)之后的最大元素之和(也就是之前的最小元素之和)
long long ans = solver(nums);
for_each(nums.begin(), nums.end(), [](int& x)
{x = -x;});
ans += solver(nums);
return ans;
}
};
lcr001
递归+倍增 实现整数除法
先处理正负号和边界情况(如除数为±1、结果溢出)
再通过每次将除数翻倍来快速计算商
避免直接循环相减效率低
class Solution {
public:
//利用减法实现除法 均已保证传入的a,b是负数
unsigned int div(int a, int b)
{
int res=0;
while(a<=b){//a的绝对值大
int temp=b;
unsigned int count=1;
while(temp>=0xc0000000&&a<=temp+temp){
count+=count;//可以减的次数翻倍
temp+=temp;//减数也翻倍
}
res+=count;
a-=temp;
}
return res;
}
int divide(int a, int b) {
if (a == INT_MIN && b == -1) {
return INT_MAX;
}
bool positive=true;
if (a > 0) {
positive=!positive;
a = -a;
}
if (b > 0) {
positive=!positive;
b = -b;
}
unsigned int res = div(a, b);
return positive? res : -res;
}
};
lc1496
set<string> visited;
visited.insert("0,0");
class Solution {
public:
bool isPathCrossing(string path) {
int x = 0, y = 0;
// 使用集合存储已经走过的位置
set<string> visited;
visited.insert("0,0");
for (auto& p : path) {
if (p == 'N')
y++;
else if (p == 'S')
y--;
else if (p == 'W')
x--;
else
x++;
// 生成当前位置的字符串表示
string pos = to_string(x) + "," + to_string(y);
if (visited.count(pos))
return true;
visited.insert(pos);
}
return false;
}
};
lcp40.
sort+反悔贪心
选最大的 cnt 个数求和,若和为偶数直接返回;
若为奇数,就换前 cnt 个里最小的奇(偶)数与后面最大的偶(奇)数,取两种换法里的最大值。
class Solution {
public:
int maximumScore(vector<int>& cards, int cnt) {
ranges::sort(cards, greater());
int s = reduce(cards.begin(), cards.begin() + cnt); // 最大的 cnt 个数之和
if (s % 2 == 0) { // s 是偶数
return s;
}
auto replaced_sum = [&](int x) -> int {
for (int i = cnt; i < cards.size(); i++) {
if (cards[i] % 2 != x % 2) { // 找到一个最大的奇偶性和 x 不同的数
return s - x + cards[i]; // 用 cards[i] 替换 s
}
}
return 0;
};
int x = cards[cnt - 1];
int ans = replaced_sum(x); // 替换 x
for (int i = cnt - 2; i >= 0; i--) { // 前 cnt-1 个数
if (cards[i] % 2 != x % 2) { // 找到一个最小的奇偶性和 x 不同的数
ans = max(ans, replaced_sum(cards[i])); // 替换
break;
}
}
return ans;
}
};
快速选择
找数组中第k大(小)的元素,不用整体排序,更高效
class Solution {
public:
int maximumScore(vector<int>& cards, int cnt) {
ranges::nth_element(cards, cards.end() - cnt); // 快速选择
int s = reduce(cards.end() - cnt, cards.end()); // 最大的 cnt 个数之和
if (s % 2 == 0) { // s 是偶数
return s;
}
int n = cards.size();
// 加进来的最大偶数/奇数
int mx[2] = {INT_MIN / 2, INT_MIN / 2}; // 除 2 防止最下面减法溢出
for (int i = 0; i < n - cnt; i++) {
int v = cards[i];
mx[v % 2] = max(mx[v % 2], v);
}
// 要去掉的最小偶数/奇数
int mn[2] = {INT_MAX / 2, INT_MAX / 2};
for (int i = n - cnt; i < n; i++) {
int v = cards[i];
mn[v % 2] = min(mn[v % 2], v);
}
return max(s + max(mx[0] - mn[1], mx[1] - mn[0]), 0);
}
};