ybt1224 最大子矩阵
【题目描述】
已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1×1)子矩阵。
【输入】
输入是一个N×N的矩阵。输入的第一行给出N(0
【输出】
输出最大子矩阵的大小。
【输入样例】
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
【输出样例】
15
【题解】
一开始,我的思路很简单:求出所有子矩阵,O(n^4^)的复杂度,n<=100能过。(n方过一万)
但是我没有想到的是,时间不爆,但空间会爆(MLE)。而且写的程序有一堆BUG(RE、WA)。
初稿:一开始的大红大紫大蓝代码:(3AC,1WA,6MLE,6RE -> 19’)
#include
#include
#include
#include
using namespace std;
int n,ans=0,f[105][105][105][105];//四维数组,MLE源泉
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> f[1][1][i][j];
if (f[1][1][i][j] > ans) {
ans = f[1][1][i][j];//更新答案
}
}
}
for (int i = 1; i <= n; i++) {//枚举矩阵高度
for (int j = 1; j <= n; j++) {//枚举矩阵宽度
for (int k = 1; k + i - 1 <= n; k++) {//枚举点的纵坐标
for (int l = 1; l + j - 1 <= n; l++) {//枚举点的横坐标
f[i][j][k][l] = f[i - 1][j - 1][k][l] + f[1][j - 1][k + i - 1][l] + f[i - 1][1][k][l + j - 1] + f[1][1][k + i - 1][l + j - 1];//这就是求矩阵的公式,当i或j=0的时候就会触发BUG,因为f数组前两维为零时没有值。
if (f[i][j][k][l] > ans) {
ans = f[i][j][k][l];//更新答案
}
}
}
}
}
cout << ans << endl;
return 0;
}
二稿:优化之后的代码(将四维数组滚掉一维并修复了BUG)(6AC,4WA -> 60’):
优化方法:针对MLE,考虑将f的第一维滚掉,但是一开始,小一圈的矩阵+竖条+横条+点的算法无法滚掉一个维度,所以优化算法,将求矩阵的和的算法改为:短一块的矩阵+横条。
但是这种算法不适用于0维的点和1维的线,所以f数组只存长宽>=2的矩阵,0维的点和1维的线都另开数组来计算。
用minif_1存横线,minif_2存竖线,spmnf(superminif)存点。
#include
#include
#include
#include
using namespace std;
int n,ans=0/*因为矩阵有负数,所以如果答案为负,则无法得到正确答案*/,f[105][105][105],minif_1[105][105][105],minif_2[105][105][105],spmnf[105][105]/*superminif*/;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {//处理零维区间-点
for (int j = 1; j <= n; j++) {
cin >> spmnf[i][j];
if (spmnf[i][j] > ans) {
ans = spmnf[i][j];
}
}
}
for (int i = 2; i <= n; i++) {//处理一维区间-线//枚举长度
for (int j = 1; j + i - 1 <= n; j++) {//处理第一维坐标
for (int k = 1; k <= n; k++) {//处理第二维坐标
minif_1[i][k][j] = minif_1[i - 1][k][j] + spmnf[k][j + i - 1];
if (minif_1[i][k][j] > ans) {
ans = minif_1[i][k][j];
}
minif_2[i][j][k] = minif_2[i - 1][j][k] + spmnf[j + i - 1][k];
if (minif_2[i][j][k] > ans) {
ans = minif_2[i][j][k];
}
}
}
}
for (int i = 2; i <= n; i++) {//处理二维区间//枚举矩阵高度
for (int j = 2; j <= n; j++) {//枚举矩阵宽度
for (int k = 1; k + i - 1 <= n; k++) {//枚举点的纵坐标
for (int l = 1; l + j - 1 <= n; l++) {//枚举点的横坐标
if ((j>2)&&(i>2)) {//当完全可以这样算的时候
f[j][k][l] = f[j][k][l] + minif_1[j][k + i - 1][l];
}
else {
if (i == 2) {//这时可以直接由两条横线组成(f内不存在高度为1的矩阵)
f[j][k][l] = minif_1[j][k][l] + minif_1[j][k + 1][l];
}
else {//j=2,i>2,这时可以有两条竖线组成
f[j][k][l] = minif_2[i][k][l] + minif_2[i][k][l + 1];
}
}
if (f[j][k][l] > ans) {
ans = f[j][k][l];
}
}
}
}
}
cout << ans << endl;
return 0;
}
然后将ans初值调整到-inf。
三稿:修改最后答案小于0的BUG(7AC,3WA -> 70’)
#include
#include
#include
#include
using namespace std;
int n,ans=-0x3fffffff/*修改初值为-inf*/,f[105][105][105],minif_1[105][105][105],minif_2[105][105][105],spmnf[105][105];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {//处理零维区间
for (int j = 1; j <= n; j++) {
cin >> spmnf[i][j];
if (spmnf[i][j] > ans) {
ans = spmnf[i][j];
}
}
}
for (int i = 2; i <= n; i++) {//处理一维区间//枚举长度
for (int j = 1; j + i - 1 <= n; j++) {//处理第一维坐标
for (int k = 1; k <= n; k++) {//处理第二维坐标
minif_1[i][k][j] = minif_1[i - 1][k][j] + spmnf[k][j + i - 1];
if (minif_1[i][k][j] > ans) {
ans = minif_1[i][k][j];
}
minif_2[i][j][k] = minif_2[i - 1][j][k] + spmnf[j + i - 1][k];
if (minif_2[i][j][k] > ans) {
ans = minif_2[i][j][k];
}
}
}
}
for (int i = 2; i <= n; i++) {//处理二维区间//枚举矩阵高度
for (int j = 2; j <= n; j++) {//枚举矩阵宽度
for (int k = 1; k + i - 1 <= n; k++) {//枚举点的纵坐标
for (int l = 1; l + j - 1 <= n; l++) {//枚举点的横坐标
if ((j>2)&&(i>2)) {//够大
f[j][k][l] = f[j][k][l] + minif_1[j][k + i - 1][l];
}
else {
if (i == 2) {
f[j][k][l] = minif_1[j][k][l] + minif_1[j][k + 1][l];
}
else {
f[j][k][l] = minif_2[i][k][l] + minif_2[i][k][l + 1];
}
}
if (f[j][k][l] > ans) {
ans = f[j][k][l];
}
}
}
}
}
cout << ans << endl;
return 0;
}
发现处理一维矩阵时有类似二维的BUG,也就是不存在长度为一的线
那么线=两个零维点。
终稿:AC代码(10AC -> 100’)
#include
#include
#include
#include
using namespace std;
int n,ans=-0x3fffffff/*修改初值为-inf*/,f[105][105][105],minif_1[105][105][105],minif_2[105][105][105],spmnf[105][105];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {//处理零维区间
for (int j = 1; j <= n; j++) {
cin >> spmnf[i][j];
if (spmnf[i][j] > ans) {
ans = spmnf[i][j];
}
}
}
for (int i = 2; i <= n; i++) {//处理一维区间//枚举长度
for (int j = 1; j + i - 1 <= n; j++) {//处理第一维坐标
for (int k = 1; k <= n; k++) {//处理第二维坐标
if (i == 2) {//只要长度为二,就等于两个0维点相加
minif_1[i][k][j] = spmnf[k][j] + spmnf[k][j + 1];
if (minif_1[i][k][j] > ans) {
ans = minif_1[i][k][j];
}
minif_2[i][j][k] = spmnf[j][k] + spmnf[j + 1][k];
if (minif_2[i][j][k] > ans) {
ans = minif_2[i][j][k];
}
}
else {
minif_1[i][k][j] = minif_1[i - 1][k][j] + spmnf[k][j + i - 1];
if (minif_1[i][k][j] > ans) {
ans = minif_1[i][k][j];
}
minif_2[i][j][k] = minif_2[i - 1][j][k] + spmnf[j + i - 1][k];
if (minif_2[i][j][k] > ans) {
ans = minif_2[i][j][k];
}
}
}
}
}
for (int i = 2; i <= n; i++) {//处理二维区间//枚举矩阵高度
for (int j = 2; j <= n; j++) {//枚举矩阵宽度
for (int k = 1; k + i - 1 <= n; k++) {//枚举点的纵坐标
for (int l = 1; l + j - 1 <= n; l++) {//枚举点的横坐标
if ((j>2)&&(i>2)) {//够大
f[j][k][l] = f[j][k][l] + minif_1[j][k + i - 1][l];
}
else {
if (i == 2) {
f[j][k][l] = minif_1[j][k][l] + minif_1[j][k + 1][l];
}
else {
f[j][k][l] = minif_2[i][k][l] + minif_2[i][k][l + 1];
}
}
if (f[j][k][l] > ans) {
ans = f[j][k][l];
}
}
}
}
}
cout << ans << endl;
return 0;
}