7-51 子矩阵(NOIP-2014-普及组-4) 分数 100 作者 严华云 单位 湖州师范学院 题目.png 输入格式: 第一行包含用空格隔开的四个整数n,m,r,c,意义如问题描述中所述,每两个整数之间用一个空格隔开。 接下来的n行,每行包含m个用空格隔开的整数,用来表示问题描述中那个n行m列的矩阵。 输出格式: 输出共1行,包含1个整数,表示满足题目描述的子矩阵的最小分值。 输入样例: 5 5 2 3 9 3 3 3 9 9 4 8 7 4 1 7 4 6 6 6 8 5 6 9 7 4 5 6 1 输出样例: 6 【输入输出样例 1 说明】 该矩阵中分值最小的2行3列的子矩阵由原矩阵的第4行、第5行与第1列、第3列、第4列交叉位置的元素组成,为 21.png ,其分值为|6-5|+|5-6|+|7-5|+|5-6|+|6-7|+|5-5|+|6-6|=6。 【数据说明】 对于50%的数据,1≤n≤12,1≤m≤12, 矩阵中的每个元素1≤a[i][j]≤20; 对于100%的数据,1≤n≤16,1≤m≤16,矩阵中的每个元素1≤a[i][j]≤1000,1≤r≤n,1≤c≤m。
时间: 2024-04-02 22:32:42 浏览: 85
首先,我们可以暴力枚举所有可能的子矩阵,并计算它们的分值,最后选出分值最小的一个。具体来说,对于每个子矩阵,我们可以先计算出它的每个元素与它所在行和列的中位数之差的绝对值,然后将这些绝对值相加即可得到该子矩阵的分值。时间复杂度为 $O(n^6)$,无法通过本题。
注意到题目中给定了 $r$ 和 $c$,因此我们可以通过预处理每个元素所在行和列的中位数,将每个元素与其所在行和列的中位数之差的绝对值预处理出来,从而将计算子矩阵分值的时间复杂度优化到 $O(n^4)$。具体来说,我们可以使用前缀和的思想,先预处理出每一行和每一列的前缀和,然后对于每个元素,可以通过前缀和求出它所在行和列的中位数,从而计算出它与它所在行和列的中位数之差的绝对值。时间复杂度为 $O(n^2)$。
接下来,我们考虑如何高效地枚举所有可能的子矩阵。我们可以枚举子矩阵的左边界和右边界,然后利用前缀和快速计算出该子矩阵内每一行的和以及每一列的和,从而在 $O(n)$ 的时间内计算出该子矩阵的和。具体来说,我们可以使用前缀和的思想,先预处理出每一行和每一列的前缀和,然后对于每个子矩阵,可以通过前缀和求出该子矩阵内每一行的和以及每一列的和,从而计算出该子矩阵的和。时间复杂度为 $O(n^4)$。
综上所述,总时间复杂度为 $O(n^4)$,可以通过本题。具体实现时,可以先预处理出每个元素与其所在行和列的中位数之差的绝对值,然后再枚举子矩阵的左边界和右边界,利用前缀和计算出该子矩阵的和并更新答案。
相关问题
c++写7-51 子矩阵(NOIP-2014-普及组-4) 分数 100 作者 严华云 单位 湖州师范学院 题目.png 输入格式: 第一行包含用空格隔开的四个整数n,m,r,c,意义如问题描述中所述,每两个整数之间用一个空格隔开。 接下来的n行,每行包含m个用空格隔开的整数,用来表示问题描述中那个n行m列的矩阵。 输出格式: 输出共1行,包含1个整数,表示满足题目描述的子矩阵的最小分值。 输入样例: 5 5 2 3 9 3 3 3 9 9 4 8 7 4 1 7 4 6 6 6 8 5 6 9 7 4 5 6 1 输出样例: 6 【输入输出样例 1 说明】 该矩阵中分值最小的2行3列的子矩阵由原矩阵的第4行、第5行与第1列、第3列、第4列交叉位置的元素组成,为 21.png ,其分值为|6-5|+|5-6|+|7-5|+|5-6|+|6-7|+|5-5|+|6-6|=6。 【数据说明】 对于50%的数据,1≤n≤12,1≤m≤12, 矩阵中的每个元素1≤a[i][j]≤20; 对于100%的数据,1≤n≤16,1≤m≤16,矩阵中的每个元素1≤a[i][j]≤1000,1≤r≤n,1≤c≤m。
```c++
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;
int n, m, r, c;
int a[N][N], row_median[N][N], col_median[N][N], abs_diff[N][N][N][N];
void init() // 预处理每个元素与其所在行和列的中位数之差的绝对值
{
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
{
int row_sum = 0, cnt = 0;
for (int k = j; k <= j + c - 1 && k <= m; k ++ ) // 求该元素所在行的和
{
row_sum += a[i][k];
cnt ++ ;
}
int row_avg = (row_sum + cnt - 1) / cnt; // 求该元素所在行的平均数
row_median[i][j] = row_avg; // 该元素所在行的中位数
}
for (int j = 1; j <= m; j ++ )
for (int i = 1; i <= n; i ++ )
{
int col_sum = 0, cnt = 0;
for (int k = i; k <= i + r - 1 && k <= n; k ++ ) // 求该元素所在列的和
{
col_sum += a[k][j];
cnt ++ ;
}
int col_avg = (col_sum + cnt - 1) / cnt; // 求该元素所在列的平均数
col_median[i][j] = col_avg; // 该元素所在列的中位数
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
for (int x = i; x <= i + r - 1 && x <= n; x ++ )
for (int y = j; y <= j + c - 1 && y <= m; y ++ )
abs_diff[i][j][x][y] = abs(a[x][y] - row_median[i][j]) + abs(a[x][y] - col_median[x][y]); // 该元素与其所在行和列的中位数之差的绝对值
}
int calc(int lx, int ly, int rx, int ry) // 计算子矩阵的分值
{
int res = 0;
for (int i = lx; i <= rx; i ++ ) // 计算每一行的和
res += col_median[i][ly] * (ry - ly + 1) - col_median[i][ly] * (ry - ly) / 2 + col_median[i][ry] * (ry - ly + 1) - col_median[i][ry] * (ry - ly) / 2;
for (int j = ly; j <= ry; j ++ ) // 计算每一列的和
res += row_median[lx][j] * (rx - lx + 1) - row_median[lx][j] * (rx - lx) / 2 + row_median[rx][j] * (rx - lx + 1) - row_median[rx][j] * (rx - lx) / 2;
for (int i = lx; i <= rx; i ++ )
for (int j = ly; j <= ry; j ++ )
res += abs_diff[lx][ly][i][j]; // 计算该子矩阵内每个元素与其所在行和列的中位数之差的绝对值之和
return res;
}
int main()
{
cin >> n >> m >> r >> c;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
cin >> a[i][j];
init(); // 预处理每个元素与其所在行和列的中位数之差的绝对值
int res = 0x3f3f3f3f;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
for (int x = i; x <= n; x ++ )
for (int y = j; y <= m; y ++ )
if (x - i + 1 >= r && y - j + 1 >= c) // 子矩阵大小满足要求
res = min(res, calc(i, j, x, y)); // 更新答案
cout << res << endl;
return 0;
}
```
已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1*1)子矩阵。比如,如下4*4的矩阵 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2 的最大子矩阵是 9 2 -4 1 -1 8 这个子矩阵的大小是15。 输入描述: 输入是一个N*N的矩阵。输入的第一行给出N (0<N<=100)。再后面的若干行中,依次(首先从左到右给出第一行的N个整数,再从左到右给出第二行的N个整数……)给出矩阵中的N2个整数,整数之间由空白字符分隔(空格或者空行)。已知矩阵中整数的范围都在[-127, 127]。注意,样例输入中,的确有一个空行。 输出描述: 输出最大子矩阵的大小。 样例输入: 样例输出: 4 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1
这是一个经典的最大子矩阵问题,可以通过动态规划来解决。我们可以先把每一行的元素加起来,形成一个一维数组,然后对这个一维数组进行最大连续子序列和的计算,这个最大连续子序列和就是以这一行为底的最大子矩阵的大小。最后,我们可以在所有以每一行为底的最大子矩阵中找到最大的那个,就是整个矩阵的最大子矩阵。具体实现可以参考下面的代码:
```python
import sys
n = int(input().strip())
matrix = []
for i in range(n):
row = list(map(int, input().strip().split()))
matrix.append(row)
# 计算以一维数组为底的最大子矩阵大小
def max_subarray(nums):
max_sum = -sys.maxsize
cur_sum = 0
for num in nums:
cur_sum += num
max_sum = max(max_sum, cur_sum)
cur_sum = max(cur_sum, 0)
return max_sum
# 计算以每一行为底的最大子矩阵大小
def max_submatrix(matrix):
n = len(matrix)
max_size = -sys.maxsize
for i in range(n):
nums = [0] * n
for j in range(i, n):
for k in range(n):
nums[k] += matrix[j][k]
cur_size = max_subarray(nums)
max_size = max(max_size, cur_size)
return max_size
print(max_submatrix(matrix))
```
这个算法的时间复杂度是 $O(n^3)$,在 $n$ 不是太大的时候可以通过本题。
阅读全文