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;
}