阅读 101

leetcode 2033. 获取单值网格的最小操作数 题解(两个重要知识点,取中位数好理解一点)

这道题有两个知识点个人认为比较重要。

第一个:

要使任意两元素最终相等,这两元素的差必须是 x 的倍数,否则无法通过加减 x 来相等

亦即:要使两个元素通过加减x来变成相等,它们之间的差一定是x的倍数吗???也就是说上面这个知识点是充要条件吗?

证明:

这个证明比较简单。

假设有两个整数a,b和x。要使得a+a′x=b+b′xb−a=a′x−b′x有b−a=(a′−b′)x。易知a′,b′和x都是整数,所以是成立的假设有两个整数a,b和x。\\ 要使得a + a'x = b + b'x \\ b - a = a'x - b'x \\ 有b - a = (a'-b')x。 易知a',b'和x都是整数,所以是成立的abx使a+ax=b+bxba=axbxba=(ab)xa,bx

结合一下同余定理可知要令最后各个数通过加减x来最终相等,那么可以:

我们可以以数组中的某一元素为基准,若所有元素与它的差均为 x 的倍数,则任意两元素之差为 x 的倍数

第二个:

参考这一个题解:[java]贪心 取中位数(垃圾解释) - 获取单值网格的最小操作数 - 力扣(LeetCode) (leetcode-cn.com)

1633843495-oEjmUY-bd68d7f4e59501f83d7cb4643d620e7.jpg   图来源于[java]贪心 取中位数(垃圾解释) - 获取单值网格的最小操作数 - 力扣(LeetCode) (leetcode-cn.com)

这个问题的证明有点像初中数学:求一个点,到直线上的各个点之间的距离之和最小。

具体证明省略。。。

可参考大神的证明:贪心取中位数的理由 - 获取单值网格的最小操作数 - 力扣(LeetCode) (leetcode-cn.com)

结合上面的两个知识点可知:中位数和中位数之间的所有整数到各个点之间的距离之和最小。但是,数组中的数不一定能通过加减x最终变成,比如输入数组[2,4,6,8],x=2。虽然5也是到各个点之和最小,但是数组中的数并不能通过加减x变成5。因此我们直接取中位数就好了。

只要数组中的每个数与数组中的某个数的差是x的倍数, 那么每一个数都能通过加减x变成中位数

中位数解法:

    public int minOperations(int[][] grid, int x) {         int[] nums = new int[grid.length * grid[0].length];         int max = Integer.MIN_VALUE;         int min = Integer.MAX_VALUE;         int idx = 0;                  // 校验         for (int i = 0; i < grid.length; i++) {             for (int j = 0; j < grid[0].length; j++) {                 nums[idx++] = grid[i][j];                 max = Math.max(grid[i][j], max);                 min = Math.min(grid[i][j], min);                 if ((grid[i][j] - grid[0][0]) % x != 0) {                     return -1;                 }             }         }         // 先排序         Arrays.sort(nums);                 int mid = nums[nums.length / 2];         int result = 0;         for (int i = 0; i < nums.length; i++) {             result += Math.abs((mid - nums[i]) / x);         }         return result;     } 复制代码

前缀和解法:

public int minOperations(int[][] grid, int x) {         int[] nums = new int[grid.length * grid[0].length];         int max = Integer.MIN_VALUE;         int min = Integer.MAX_VALUE;         int idx = 0;         for (int i = 0; i < grid.length; i++) {             for (int j = 0; j < grid[0].length; j++) {                 nums[idx++] = grid[i][j];                 max = Math.max(grid[i][j], max);                 min = Math.min(grid[i][j], min);                 if ((grid[i][j] - grid[0][0]) % x != 0) {                     return -1;                 }             }         }         // 先排序,在求前缀和         Arrays.sort(nums);         int[] prefixSum = new int[grid.length * grid[0].length + 1];         for (int i = 1; i < prefixSum.length; i++) {             prefixSum[i] = prefixSum[i - 1] + nums[i - 1];         }         int result = Integer.MAX_VALUE;         for (int i = 0; i < nums.length; i++) {             // 左边             int deltaLeftSum = (i + 1) * nums[i] - (prefixSum[i + 1] - prefixSum[0]);             // 右边             int deltaRightSum = (prefixSum[nums.length] - prefixSum[i + 1]) - (nums.length - i - 1) * nums[i];             result = Math.min(result, (deltaRightSum + deltaLeftSum) / x);         }         return result;     }


作者:壁咚王我当定了
链接:https://juejin.cn/post/7018171309116882951


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐