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

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

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

题目描述

给你一个长度为 n n n 的整数数组 n u m s nums nums n n n 是 偶数 ,同时给你一个整数 k k k

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

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

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

数据范围
  • 2 < = n = = n u m s . l e n g t h < = 1 0 5 2 <= n == nums.length <= 10^5 2<=n==nums.length<=105
  • n n n 是偶数
  • 0 < = n u m s [ i ] < = k < = 1 0 5 0 <= nums[i] <= k <= 10^5 0<=nums[i]<=k<=105
解题思路

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

对于 ( a , b ) (a,b) (a,b),令 m = m a x ( a , b , k − a , k − b ) m=max(a,b,k-a,k-b) m=max(a,b,ka,kb)

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

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

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

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

注意,只需一次操作就可以将 ( a , b ) (a,b) (a,b) 的差值变成 0 0 0,所以,操作次数不会超过 n n 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(n+k) O(n+k)

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

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

题目描述

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

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

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

数据范围
  • 1 < = n = = g r i d . l e n g t h < = 100 1 <= n == grid.length <= 100 1<=n==grid.length<=100
  • n = = g r i d [ i ] . l e n g t h n == grid[i].length n==grid[i].length
  • 0 < = g r i d [ i ] [ j ] < = 1 0 9 0 <= grid[i][j] <= 10^9 0<=grid[i][j]<=109
解题思路

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

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

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

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

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

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

对于递增的情况, d p [ j ] [ i ] [ f ] = m a x ( d p [ j − 1 ] [ x ] [ 1 ] ) + s u m ( g [ y ] [ j − 1 ] ) dp[j][i][f] = max(dp[j-1][x][1])+sum(g[y][j-1]) dp[j][i][f]=max(dp[j1][x][1])+sum(g[y][j1]),其中, 0 ≤ x ≤ i 0≤x≤i 0xi x + 1 ≤ y ≤ i x+1≤y≤i x+1yi,因第 j j j 列涂黑前 i i i 行而增加的分数为 s u m ( g [ y ] [ j − 1 ] ) sum(g[y][j-1]) sum(g[y][j1])

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

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

  • t = m a x ( d p [ j − 2 ] [ x ] [ 0 ] , d p [ j − 2 ] [ x ] [ 1 ] ) + s u m ( g [ y ] [ j − 1 ] ) t = max(dp[j-2][x][0],dp[j-2][x][1]) + sum(g[y][j-1]) t=max(dp[j2][x][0],dp[j2][x][1])+sum(g[y][j1])

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

其中, 0 ≤ x ≤ n 0≤x≤n 0xn y = m a x ( x , i ) y = max(x,i) y=max(x,i)

d p [ j ] [ i ] [ f ] dp[j][i][f] 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^3) O(n3)

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

END

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

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

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

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值