小美的平衡矩阵(前缀和例题 -- 美团笔试编程题)

        2024美团春招,被这一题给难住了 美团校招笔试真题_Java工程师、C++工程师_牛客网

题目:

        

        

解答:

        这道题的关键点就是要计算出以某一点为矩阵右下角时,1的个数

        我一开始是想着遍历,以某一点为起点(矩阵左上角),遍历i*i的矩阵,但是要用五层循环,超时了。然后又想着将之前的矩阵的结果保留下来,这样计算新矩阵时就能大大减少遍历次数,只用查看前面矩阵的结果来推算出当前矩阵的结果,可是我用的是动态规划,会计算左边和上边的值,这有一个问题,就是左上角对角线位置的值会被计算两次,因为上边的左边算了一次,左边的上边也算了一次。后来看了别人的代码,发现只要动态规划时只计算一边,即上边或者左边的值,另一个值在本次循环的时候单独计算就能解决了,这就是前缀和,用于在数组或矩阵中快速计算某个区间内的元素和。

        这道题的思路就是,用前缀和计算出从左上角[0,0]到右下角[x,y]这个范围内的矩阵的值,这样,如果想要计算以[x,y]为终点的 i*i 的矩阵的值,就只需要用[x,y]的值减去[x-i,y]和[x,y-1]的值再加上以[x-i,y]为终点、以[x,y-1]为终点这两个矩阵的重合部分(刚刚被减了两次)就行了。

        之后再对结果进行判断,就能知道以[x,y]为终点的 i*i 的矩阵是否是平衡矩阵。如果是则记录。

        这种方法只有三层循环,大大降低了时间复杂度。

int main() {
    int n = 4;
    vector<string> matrix(n);
    matrix[0] = "1010";
    matrix[1] = "0101";
    matrix[2] = "1100";
    matrix[3] = "0011";


    vector<vector<int>> dp(n + 1, vector<int>(n + 1));

    for (int i = 0; i < n; i++) {
        int sum = 0;
        //动态规划,但是如果又加上面又加左边会导致对角线左上方被算两次,所以只动态规划算上面,左边的单独在本趟算
        for (int j = 0; j < n; j++) {
            if (matrix[i][j] == '1') sum++;
            dp[i + 1][j + 1] = sum + dp[i][j + 1];
        }
    }


    vector<int> res(n + 1);

    //i*i的矩阵
    for (int i = 1; i <= n; i++) {
        //当矩阵内元素个数为奇数,一定不平衡
        //一个奇数的平方一定是奇数
        if (i % 2 != 0) {
            res[i] = 0;
            continue;
        }
        //以[x,y]为起点的矩阵,其右下角值=以其右下角为终点的矩阵的1的个数
        //x+i-1 -- i为2时,计算x和x+1位置,那么包括x位置在内,总共i个位置需要合法,也就是接下来的i-1个位置需要合法
        for (int x = 1; x + i - 1 <= n; x++) {
            for (int y = 1; y + i - 1 <= n; y++) {
                //[x+i-1][y+i-1]的值是从[1,1]到[x+i-1][y+i-1]的值
                //  那么如果要计算以[x+i-1][y+i-1]为终点的i*i矩阵1的值,需要减去以[x+i-1][y-1]为终点的矩阵2的值和以[x-1][y+i-1]为终点的矩阵3的值
                //  这还不够,矩阵2和矩阵3有一个重合的区域,如果只是减的话,重合的区域会被减两次,所以减完后还需要加上一个重合的区域的值,也就是以[x-1][y-1]为终点的矩阵4的值
                int val = dp[x + i - 1][y + i - 1] - dp[x + i - 1][y - 1] - dp[x - 1][y + i - 1] + dp[x - 1][y - 1];
                if (val == (i * i) / 2) {
                    res[i]++;
                }
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        cout << res[i] << endl;
    }


    return 0;
}

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值