1253奇数值单元格的数目——Leetcode天天刷【纯暴力+数学】(2022.7.12)

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:xyValue: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 xValuexValueans=xValue(nyValue)+(mxValue)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;
}

后话

今天的任务达标了,搞别的了。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值