3587.最小相邻交换至奇偶交替
原文链接:算法竞赛>力扣>双周赛 | biweekly-contest-159
解题思路
可以移动奇数,也可以移动偶数,二者等价,不妨移动奇数。
可以将奇数移动到奇数位置,也可以将奇数移动到偶数位置,取较小值。
先考虑移动到奇数位置
将最左边的奇数移动到最左边奇数位置,将第二最左边的奇数移动到第二最左边奇数位置,以此类推。这样需要移动的次数最少。
奇数的数量和偶数的数量相差不能超过 1,否则不存在有效排列。
代码实现
int minSwaps(vector<int>& nums) {
// 奇数所在位置
vector<int> p;
for (int i = 0; i < nums.size(); i++)
if (nums[i] & 1)p.push_back(i);
// 奇数数 偶数数
int m = p.size(), n = nums.size() - m;
// 相差大于1则无法形成有效排列
if (abs(m - n) > 1)return -1;
// false 移动到偶数下标 true 移动到奇数下标
auto sv = [&](bool f) {
// 要移动到偶数下标,那么p必须是多的一方
if (!f && m < n)return INT_MAX;
// 要移动到奇数下标,那么p必须是少的一方
if (f && m > n)return INT_MAX;
int res = 0;
for (int i = 0; i < m; i++)
res += abs(p[i] - (i * 2 + f));
return res;
};
return min(sv(false), sv(true));
}
时间复杂度 ,空间复杂度 。
3588.找到最大三角形面积
解题思路
找到最小的x、最大的x、最小的y、最大的y。
找到每个x下的最大y和最小y,找到每个y下的最大x和最小x。
代码实现
long long maxArea(vector<vector<int>>& coords) {
typedef long long ll;
// xyMin x下y的最小值; xyMax x下y的最大值;yxMin y下x的最小值;yxMax y下x的最大值
unordered_map<int, int> xyMin, xyMax, yxMin, yxMax;
int minX = 1e7, maxX = 0, minY = 1e7, maxY = 0;
for (auto v : coords) {
int x = v[0], y = v[1];
if (!xyMin.count(x) || xyMin[x] > y)xyMin[x] = y;
if (!xyMax.count(x) || xyMax[x] < y)xyMax[x] = y;
if (!yxMin.count(y) || yxMin[y] > x)yxMin[y] = x;
if (!yxMax.count(y) || yxMax[y] < x)yxMax[y] = x;
if (minX > x)minX = x;
if (maxX < x)maxX = x;
if (minY > y)minY = y;
if (maxY < y)maxY = y;
}
ll res = 0;
for (auto [x,yMin] : xyMin)
res = max(res, 1LL * (xyMax[x] - yMin) * max(maxX - x, x - minX));
for (auto [y,xMin] : yxMin)
res = max(res, 1LL * (yxMax[y] - xMin) * max(maxY - y, y - minY));
return res ? res : -1;
}
时间复杂度 ,空间复杂度 。
3589.计数质数间隔平衡子数组
解题思路
双指针维护窗口,窗口条件为:
- 包含至少两个质数
- 最大和最小质数的差小于或等于 k
因此,需要知道三个信息:当前窗口的所有质数、当前窗口的最大质数、当前窗口的最小质数。
- 当前窗口的所有质数使用队列维护
- 当前窗口的最大质数使用单调递减的单调栈维护,栈底即为最大质数
- 当前窗口的最小质数使用单调递增的单调栈维护,栈底即为最小质数
右指针每次移动,维护窗口,使得窗口符合条件,假设当前窗口的左右端点为 l、r。子数组的右端点固定为r,子数组的左端点的范围是什么呢?由于子数组包含至少两个质数,设上上个质数(即窗口倒数第二个质数)的位置为 last,则子数组的右端点的范围即为 [l,last],故 res+=last-l+1。
代码实现
static constexpr int N = 5e4 + 1;
int p[N], idx;
bool np[N];
void initPrime() {
memset(np, 0, sizeof(np));
np[0] = np[1] = true;
idx = 0;
for (int i = 2; i < N; ++i) {
if (!np[i]) p[idx++] = i;
for (int j = 0, k = N / i; p[j] <= k; ++j) {
np[p[j] * i] = true;
if (i % p[j] == 0)break;
}
}
}
int primeSubarray(vector<int>& nums, int k) {
initPrime();
// q 当前窗口质数;qMax 单调递减的单调栈;qMin 单调递增的单调栈
deque<int> q, qMax, qMin;
vector<int>& v = nums;
int n = nums.size(), res = 0, l = 0;
for (int r = 0; r < n; r++) {
// 质数
if (!np[v[r]]) {
// 当前窗口所有质数
q.push_back(r);
// 比当前质数小的栈顶元素出栈,栈底即为当前窗口的最大质数
while (!qMax.empty() && v[qMax.back()] <= v[r])qMax.pop_back();
qMax.push_back(r);
// 比当前质数大的栈顶元素出栈,栈底即为当前窗口的最小质数
while (!qMin.empty() && v[qMin.back()] >= v[r])qMin.pop_back();
qMin.push_back(r);
}
// 最大质数和最小质数的差不超过k
while (!q.empty() && v[qMax.front()] - v[qMin.front()] > k) {
// 左指针移动,尝试使得窗口满足最大质数和最小质数的差不超过k
l = q.front() + 1;
q.pop_front();
// 移除qMax中在窗口外的质数
while (!qMax.empty() && qMax.front() < l)qMax.pop_front();
// 移除qMin中在窗口外的质数
while (!qMin.empty() && qMin.front() < l)qMin.pop_front();
}
// 窗口至少包含两个质数
if (q.size() > 1) {
// 上上个质数的位置,即倒数第二个质数的位置
const int last2 = q[q.size() - 2];
// 子数组右端点为r,左端点从l到last2均可
res += last2 - l + 1;
}
}
return res;
}
时间复杂度 ,空间复杂度 。