力扣第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场夜喵双周赛
文章声明:题目来源 力扣 平台,如有侵权,请联系删除!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。