[数学:容斥原理] 1252. 奇数值单元格的数目

116 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

每日刷题 2022.08.06

题目

  • 给你一个 m x n 的矩阵,最开始的时候,每个单元格中的值都是 0。
  • 另有一个二维索引数组 indices,indices[i] = [ri, ci] 指向矩阵中的某个位置,其中 ri 和 ci 分别表示指定的行和列(从 0 开始编号)。
  • 对 indices[i] 所指向的每个位置,应同时执行下述增量操作:
    • ri 行上的所有单元格,加 1 。
    • ci 列上的所有单元格,加 1 。
  • 给你 m、n 和 indices 。请你在执行完所有 indices 指定的增量操作后,返回矩阵中 奇数值单元格 的数目。

示例

  • 示例1 image.png
输入:m = 2, n = 3, indices = [[0,1],[1,1]]
输出:6
解释:最开始的矩阵是 [[0,0,0],[0,0,0]]。
第一次增量操作后得到 [[1,2,1],[0,1,0]]。
最后的矩阵是 [[1,3,1],[1,3,1]],里面有 6 个奇数。
  • 示例2 image.png
输入: m = 2, n = 2, indices = [[1,1],[0,0]]
输出: 0
解释: 最后的矩阵是 [[2,2],[2,2]],里面没有奇数。

提示

  • 1 <= m, n <= 50
  • 1 <= indices.length <= 100
  • 0 <= ri < m
  • 0 <= ci < n

进阶

  • 你可以设计一个时间复杂度为 O(n + m + indices.length) 且仅用 O(n + m)额外空间的算法来解决此问题吗?

解题思路

  • 因为题目的数据范围并不是很大,所以直接暴力模拟就可以实现。
  • 除了暴力模拟以外的其他的方法讲解:
  • 方法一:对于矩阵中的每一个格子中的元素,都有一个属于自己的行和列号,因此只需要分别记录其所在的行号和列号每次被加的次数,最终将每一个格子的行号和列号相加,如果是奇数,那么就需要记录。这种方法相较于直接暴力模拟的方法,节省了空间,直接使用两个一维数组来分别记录行号和列号即可。
  • 方法二:首先需要统计每一行和每一列都被加了多少次,然后从中计算出被加了奇数次的行数和列数。
    • 那么使用容斥原理可知:每一行会有n个数变成奇数,每一列会有m个数会变成奇数,这样的话每一个行和列的交点都是偶数(不符合题意)。因此需要给每一个奇数行配上偶数列,每一个偶数行配上奇数列。
    • 奇数加奇数一定是偶数;奇数加偶数一定是奇数

AC代码

  • 方法一
/**
 * @param {number} m
 * @param {number} n
 * @param {number[][]} indices
 * @return {number}
 */
var oddCells = function(m, n, indices) {
  // 因为不需要知道每一个到底是什么值,因此也不用完全暴力模拟
  // 因为在前面的方法上,可以进阶到下述的方法
  // 因为每个左边上的值,就是当前的行和列相加
  let row = new Array(m).fill(0), col = new Array(n).fill(0);
  for(let one of indices) {
    row[one[0]]++;
    col[one[1]]++;
  }
  let ans = 0;
  for(let i = 0; i < m; i++) {
    for(let j = 0; j < n; j++) {
      if((row[i] + col[j]) & 1) ans++;      
    }
  }
  return ans;
};
  • 方法二
/**
 * @param {number} m
 * @param {number} n
 * @param {number[][]} indices
 * @return {number}
 */
var oddCells = function(m, n, indices) {
  // 还可以再优化,通过分析后,解决全部遍历一遍,挨个判断其是否为奇数的方法
  // 奇数的总次数 = 横奇数 + 列偶数 或者 横偶数 + 列奇数
  let row = new Array(m).fill(0), col = new Array(n).fill(0);
  for(let one of indices) {
    row[one[0]]++;
    col[one[1]]++;
  }
  let oddR = 0, oddL = 0;
  for(let r of row) {
    if(r & 1) oddR++;
  }
  for(let c of col) {
    if(c & 1) oddL++;
  }
  return oddR * (n - oddL) + (m - oddR) * oddL;
};