ybt1224 最大子矩阵(一个做了4遍的励志故事)

ybt1224 最大子矩阵

【题目描述】

已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1×1)子矩阵。

【输入】

输入是一个N×N的矩阵。输入的第一行给出N(0<N≤100)。再后面的若干行中,依次(首先从左到右给出第一行的N个整数,再从左到右给出第二行的N个整数……)给出矩阵中的N^2^个整数,整数之间由空白字符分隔(空格或者空行)。已知矩阵中整数的范围都在[−127,127]。

【输出】

输出最大子矩阵的大小。

【输入样例】

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<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
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<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
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<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
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<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
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;
}

猜你喜欢

转载自www.cnblogs.com/Wild-Donkey/p/12327801.html