头图

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

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

题目描述

给你一个长度为 n 的整数数组 numsn 是 偶数 ,同时给你一个整数 k

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

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

存在一个整数 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 趋势的情况下,分数的最大值。约定 f0 表示递减/先增后减,为 1 表示递增。

对于递增的情况, dp[j][i][f] = max(dp[j-1][x][1])+sum(g[y][j-1]),其中,0≤x≤ix+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≤ ni+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≤ny = 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场夜喵双周赛

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


字节幺零二四
9 声望5 粉丝

talk is cheap, show me you code!