zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    June 1. 2018 Week 22nd Friday
    【Android开发】【布局】几个常用布局构成的简单demo
    【Android开发】【布局】各种TabLayout样式
    【Android开发】【数据库】Realm For Android
    【Android开发】jarsigner重新打包apk
    【Android开发】Coding + git命令行基本使用
    【MarkDown】使用
    【Android Studio】Gradle统一管理版本号引用配置
    【Android开发】EasyPermissions 请求权限
    【Android开发】Android6.0请求权限方式
  • 原文地址:https://www.cnblogs.com/Wild-Donkey/p/12327801.html
Copyright © 2011-2022 走看看