1253奇数值单元格的数目——Leetcode天天刷【纯暴力+数学】(2022.7.12)
文章目录
前言
今天的题目,绿色,简单题,看完题目不考虑进阶确实很简单。
题目信息
题目链接:1252. 奇数值单元格的数目 - 力扣(LeetCode)
主要内容
简单写下题目的主要内容:
就是在一个 mxn
的 零矩阵
中,就是全0的矩阵中,我给定一组坐标数组,对于每个坐标(x, y),x对应的行的数字+1,y对应的列的数字+1,在我们执行完一系列坐标带来的+1的操作后,对整个矩阵计算 奇数值 的数目。
输入与输出
由于我们在本地IDE在需要做测试,所以我们对输入和输出有和leetcode官方有点差异。
输入
第一行,输入m,n。
第二行,输入坐标数组的长度len。
接下来len行,输入坐标x,y。
输出
一行输出,就是操作完后奇数的数目ans。
测试用例
输入
多组输入
2 3
2
0 1
1 1
2 2
2
1 1
0 0
48 37
1
40 5
输出
6
0
83
提示信息
1 <= m, n <= 50
1 <= indices.length <= 100
0 <= ri < m
0 <= ci < n
进阶要求
你可以设计一个时间复杂度为 O(n + m + indices.length)
且仅用 O(n + m)
额外空间的算法来解决此问题吗?
法一:纯暴力模拟
先不看进阶要求,那么其实这道题目的纯暴力模拟确实很简单,我们构建一个m*n的矩阵,初始化为0,然后遍历坐标,对应的行和列的单元格+1。
最后再遍历一次矩阵,就可以计算出矩阵中奇数值的数目。
时间复杂度:O(len * (m+n)), len为坐标数组的长度。
空间复杂度:O(m*n),就是矩阵的大小。
code(C++)
#include<iostream>
#include<vector>
using namespace std;
class Solution
{
public:
/// <summary>
/// 模拟
/// 思路很简单,就先根据行列生成一个矩阵
/// 然后根据参数进行数值变化
/// 然后遍历一遍矩阵,找奇数值,累加求数目和
/// </summary>
/// <param name="m">行</param>
/// <param name="n">列</param>
/// <param name="indices"></param>
/// <returns></returns>
int oddCells(int m, int n, vector<vector<int>>& indices)
{
vector<vector<int>> martix(m, vector<int>(n)); // 建立矩阵
// 遍历变换位置
for (auto& indice : indices)
{
// 获取横纵坐标
int x = indice[0], y = indice[1];
// 遍历所在的行和列,数值+1
for (int i = 0; i < n; ++i)
{
martix[x][i]++;
}
for (int i = 0; i < m; ++i)
{
martix[i][y]++;
}
}
int ans = 0;
// 遍历一遍矩阵,奇数值+1
for (int i = 0; i < m; ++i)
{
for (int j = 0; j < n; ++j)
{
ans += martix[i][j] % 2 ? 1 : 0;
}
}
return ans;
}
};
int main(int argc, char** argv)
{
int n1, m1;
Solution* sol = new Solution();
while (cin >> m1 >> n1)
{
int len;
cin >> len;
vector<vector<int>> indices(len, vector<int>(2));
for (int i = 0; i < len; ++i)
{
cin >> indices[i][0] >> indices[i][1];
}
cout << sol->oddCells(m1, n1, indices) << endl;
}
return 0;
}
显然,复杂度要求不满足进阶要求?如何实现呢,请看法二。
法二:数学+计数优化
进阶要求既是对难度有所提升,同时也为我们的思路提供了提示。
空间复杂度为m+n,很明显,其实就是行+列,我们可以用两个数组来作为行、列。
时间复杂度来说,我们遍历坐标,然后对应的行列数组中的数值+1。
那么问题来了:如何通过行、列数组中的数值来反应矩阵中的奇数数目?
首先,我们对于矩阵中的(x,y),我们通过法一中再每次+1的操作,对于单元格的数值,我们可以得出下面的式子
v
a
l
u
e
=
x
V
a
l
u
e
+
y
V
a
l
u
e
v
a
l
u
e
:
单
元
格
的
数
值
x
V
a
l
u
e
:
x
所
在
的
行
的
数
值
y
V
a
l
u
e
:
y
所
在
的
列
的
数
值
value = xValue + yValue \\ value: 单元格的数值 \\ xValue: x所在的行的数值 \\ yValue: y所在的列的数值
value=xValue+yValuevalue:单元格的数值xValue:x所在的行的数值yValue:y所在的列的数值
因此我们不难通过行,列的数值来计算单元格数值,所以时间复杂度上已经做了优化。
计数优化
回看题目,奇数,奇数,奇数!重点说3遍,重点是数字的奇偶性,所以我们只需要考虑奇偶性,而不是数值,对于行列的元素,只需要bool值来表达数字奇偶性。
再只需要考虑奇偶性的情况,对于单元格的奇偶性,它是受行和列的奇偶性影响,显然,单元格是奇数,那么所处的行和列的奇偶性一定不同。
反转上面的思路
x
V
a
l
u
e
为
奇
数
,
那
么
只
需
要
就
找
列
中
偶
数
。
x
V
a
l
u
e
为
偶
数
,
那
么
只
需
要
找
列
中
奇
数
。
∴
a
n
s
=
x
V
a
l
u
e
∗
(
n
−
y
V
a
l
u
e
)
+
(
m
−
x
V
a
l
u
e
)
∗
y
V
a
l
u
e
xValue 为奇数,那么只需要就找列中偶数。 \\ xValue 为偶数,那么只需要找列中奇数。 \\ \therefore ans = xValue * (n - yValue) + (m - xValue) * yValue
xValue为奇数,那么只需要就找列中偶数。xValue为偶数,那么只需要找列中奇数。∴ans=xValue∗(n−yValue)+(m−xValue)∗yValue
复杂度就是进阶要求
跑出的结果
code(C++)
#include<iostream>
#include<vector>
using namespace std;
class Solution
{
public:
/// <summary>
/// 数学规律
/// 要想完成题目中的进阶要求,针对空间,我们可以分别用长度为m,n的数组存储行和列
/// 遍历indices其实就是改变行列的奇偶性
/// 类比概率论中的联合和分布的关系,其实不难知道,对于每一个联合x,y
/// 要想联合为奇数值,显然x行和y列的奇偶性是互不相同的
/// 那么对于row为奇数,显然需要知道col中为偶数的情况
/// row为偶数,则需要知道col中为奇数的数量
/// </summary>
/// <param name="m"></param>
/// <param name="n"></param>
/// <param name="indices"></param>
/// <returns></returns>
int oddCells(int m, int n, vector<vector<int>>& indices)
{
vector<bool> row(m, false), col(n, false); // 建立行列数组,初始化为偶数
// 然后遍历indices,对应改变行列的奇偶性
for (auto& indice : indices)
{
// 取反
row[indice[0]] = !row[indice[0]];
col[indice[1]] = !col[indice[1]];
}
int ans = 0;
int r = 0, c = 0; // 记录行、列中的奇数
for (int i = 0; i < m; ++i)
{
r += row[i];
}
for (int i = 0; i < n; ++i)
{
c += col[i];
}
ans = r * (n - c) + (m - r) * c; // 计算奇数的总数目
return ans;
}
};
int main(int argc, char** argv)
{
int n1, m1;
Solution* sol = new Solution();
while (cin >> m1 >> n1)
{
int len;
cin >> len;
vector<vector<int>> indices(len, vector<int>(2));
for (int i = 0; i < len; ++i)
{
cin >> indices[i][0] >> indices[i][1];
}
cout << sol->oddCells(m1, n1, indices) << endl;
}
return 0;
}
后话
今天的任务达标了,搞别的了。