力扣周赛:力扣第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场夜喵双周赛
文章声明:题目来源 力扣 平台,如有侵权,请联系删除!