算法竞赛>力扣>双周赛 | biweekly-contest-159

65 阅读4分钟

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));
}

时间复杂度 O(n)O(n),空间复杂度 O(n)O(n)

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;
}

时间复杂度 O(n)O(n),空间复杂度 O(n)O(n)

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;
}

时间复杂度 O(n)O(n),空间复杂度 O(n)O(n)