力扣周赛:力扣第135场夜喵双周赛

力扣第135场夜喵双周赛:使差值相等的最少数组改动次数、网格图操作后的最大分数,涉及差分数组、动态规划、分类讨论等知识点。

T3-使差值相等的最少数组改动次数

题目描述

给你一个长度为 \(n\) 的整数数组 \(nums\)\(n\) 是 偶数 ,同时给你一个整数 \(k\)

你可以对数组进行一些操作。每次操作中,你可以将数组中 任一 元素替换为 \(0\)\(k\) 之间的 任一 整数。

执行完所有操作以后,你需要确保最后得到的数组满足以下条件:

存在一个整数 \(X\) ,满足对于所有的 \((0 <= i < n)\) 都有 \(abs(a[i] - a[n - i - 1]) = X\)
请你返回满足以上条件 最少修改次数。

数据范围

  • \(2 <= n == nums.length <= 10^5\)
  • \(n\) 是偶数
  • \(0 <= nums[i] <= k <= 10^5\)

解题思路

每对数之间不会相互影响,可以分开考虑。

对于 \((a,b)\),令 \(m=max(a,b,k-a,k-b)\)

  • 若不操作,则差值为 \(|a-b|\)
  • 若操作一次,则差值范围为 \([0,|a-b|)∪(|a-b|,m]\)
  • 若操作两次,则差值范围为 \((m,+∞]\)

由此可知,\((a,b)\) 对不同的范围有不同的贡献:

  • 对于 \([|a-b|, |a-b|]\),贡献为 \(0\)
  • 对于 \([0,|a-b|)∪(|a-b|,m]\),贡献为 \(1\)
  • 对于 \((m,+∞]\),贡献为 \(2\)

范围贡献自然联想到差分数组。

注意,只需一次操作就可以将 \((a,b)\) 的差值变成 \(0\),所以,操作次数不会超过 \(n\)

代码实现

int minChanges(vector<int> &nums, int k) {
    int n = nums.size(), f[k + 5];
    memset(f, 0, sizeof f);
    for (int i = 0, j = n - 1, d, mx; i < j; i++, j--) {
        d = abs(nums[i] - nums[j]);
        mx = max({nums[i], nums[j], k - nums[i], k - nums[j]});
        // 对于[0,|a-b|)∪(|a-b|,m],贡献为1
        f[0]++, f[d]--;
        f[d + 1]++, f[mx + 1]--;
        // 对于(m,+∞],贡献为2
        f[mx + 1] += 2;
    }
    int res = n;
    for (int x = 0, cnt = 0; x <= k; x++)
        res = min(res, cnt += f[x]);
    return res;
}

时间复杂度:\(O(n+k)\)

空间复杂度:\(O(k)\)

T4-网格图操作后的最大分数

题目描述

给你一个大小为 \(n × n\) 的二维矩阵 \(grid\) ,一开始所有格子都是白色的。一次操作中,你可以选择任意下标为 \((i, j)\) 的格子,并将第 \(j\) 列中从最上面到第 \(i\) 行所有格子改成黑色。

如果格子 \((i, j)\) 为白色,且左边或者右边的格子至少一个格子为黑色,那么我们将 \(grid[i][j]\) 加到最后网格图的总分中去。

请你返回执行任意次操作以后,最终网格图的最大总分数。

数据范围

  • \(1 <= n == grid.length <= 100\)
  • \(n == grid[i].length\)
  • \(0 <= grid[i][j] <= 10^9\)

解题思路

不考虑凹字形的黑色连通块,对于凹字形连通块,可去掉凹字形的中间一列,分数至少不会减少。

考虑单个黑色方柱连通块,对于连续的三列,黑色方柱存在如下趋势

  • 增-增,即递增
  • 减-减,即递减
  • 增-减,即先增后减
  • 减-增,即先减后增,凹字形,不考虑

考虑当前列涂黑产生的影响时,分三类讨论

  • 递增
  • 递减,先增后减
  • 上一列未涂黑(跨越连通块)

\(dp[j][i][1]\) = 考虑到第 \(j\) 列,第 \(j\) 列把前 \(i\) 行涂黑,从 第 \(j\) 列往前三列是 \(f\) 趋势的情况下,分数的最大值。约定 \(f\)\(0\) 表示递减/先增后减,为 \(1\) 表示递增。

对于递增的情况, \(dp[j][i][f] = max(dp[j-1][x][1])+sum(g[y][j-1])\),其中,\(0≤x≤i\)\(x+1≤y≤i\),因第 \(j\) 列涂黑前 \(i\) 行而增加的分数为 \(sum(g[y][j-1])\)

对于递减/先增后减的情况,\(dp[j][i][0] = max(dp[j-1][x][0],dp[j-1][x][1])+sum(g[y][j])\),其中, \(i+1≤x≤ n\)\(i+1≤y≤x\),因第 \(j\) 列涂黑前 \(i\) 行而增加的分数为 \(sum(g[y][j])\)

对于上一列未涂黑的情况,考虑上上列

  • \(t = max(dp[j-2][x][0],dp[j-2][x][1]) + sum(g[y][j-1])\)

  • $dp[j][i][0] = dp[j][i][0] = t $

其中,\(0≤x≤n\)\(y = max(x,i)\)

\(dp[j][i][f]\) 的最大值为上述三种情况的最大值。

代码实现

long long maximumScore(vector<vector<int>> &grid) {

    typedef long long ll;

    int n = grid.size();
    auto &g = grid;
    ll ps[n][n + 1], dp[n][n + 1][2];
    memset(ps, 0, sizeof ps);
    memset(dp, 0, sizeof dp);

    // 预处理每列前缀和
    // ps[j][i] = 第j列的前i个数的和
    for (int j = 0; j < n; j++) {
        for (int i = 0; i < n; i++) {
            ps[j][i + 1] = ps[j][i] + g[i][j];
        }
    }

    // 逐一考虑每一列的影响
    // 第0列无上一列,故第0列无论如何涂黑,在考虑第0列时均无影响
    for (int j = 1; j < n; j++) {

        // 递增
        // 上列涂黑前pi行
        for (int pi = 0; pi <= n; pi++)
            // 当前列涂黑前i行
            for (int i = pi; i <= n; i++)
                dp[j][i][1] = max(
                        dp[j][i][1],
                        dp[j - 1][pi][1] + ps[j - 1][i] - ps[j - 1][pi]
                );

        // 递减 或 先增后减
        // 上列涂黑前pi行
        for (int pi = 0; pi <= n; pi++)
            // 当前列涂黑前i行
            for (int i = 0; i <= pi; i++)
                dp[j][i][0] = max(
                        dp[j][i][0],
                        max(dp[j - 1][pi][0], dp[j - 1][pi][1]) + ps[j][pi] - ps[j][i]
                );

        // 上列不涂:需要查看上上列
        if (j > 1) {
            // 上上列涂黑前pi行
            for (int pi = 0; pi <= n; pi++)
                // 当前列涂前i行
                for (int i = 0; i <= n; i++) {
                    // 重新计算连通块
                    ll t = max(
                            dp[j - 2][pi][0],
                            dp[j - 2][pi][1]
                    ) + ps[j - 1][max(pi, i)];
                    dp[j][i][0] = max(dp[j][i][0], t);
                    dp[j][i][1] = max(dp[j][i][1], t);
                }
        }

    }

    ll res = 0;
    for (int i = 0; i <= n; i++)
        res = max({res, dp[n - 1][i][0], dp[n - 1][i][1]});

    return res;
}

时间复杂度:\(O(n^3)\)

空间复杂度:\(O(n^2)\)

END

文章文档:公众号 字节幺零二四 回复关键字可获取本文文档。

题目来源:力扣第135场夜喵双周赛

文章声明:题目来源 力扣 平台,如有侵权,请联系删除!

posted @ 2024-07-21 19:20  字节幺零二四  阅读(36)  评论(0)    收藏  举报