找出边界为1的最大子方阵:
1、未优化的题解:(复杂度较高)
//伪代码
max(A,N){
int n=N;
while(n>0){
for(i from 0 to N-1){
if(i+n>N) break;
l3:
for(j from 0 to N-1){
if(j+n>N) break;
//i,j就是顶点
r=i;
c=j;
//上面的边
while(c<j+n){
if(A[r][c]==0) continue l3;
c++;
}
c--;
//右边的边
while(r<i+n){
if(A[r][c]==0) continue l3;
r++;
}
r--;
//下边的边
while(c>=j){
if(A[r][c]==0) continue l3;
c--;
}
c++;
//左边的边
while(r>=i){
if(A[r][c]==0) continue l3;
r--;
}
//r++;--------- 无需恢复
return n;
}
}
n--;
}
return 0;
}
代码:
#include<iostream>
#include<vector>
using namespace std;
int solve(vector<vector<int> > A){
int N=A.size();
int n=N;
while(n>0)
{
for(int i=0;i<N;i++)
{
if(i+n>N) break;
//第二层循环:
int j=0;
l3:
while(j++<N){
if(j+n>N) break;
//检查四条边
//i,j就是顶点
int r=i,c=j;
//上面的边
while(c<j+n)
{
if(A[r][c++]==0) goto l3;
}
c--;
//右边的边
while(r<i+n)
{
if(A[r++][c]==0) goto l3;
}
r--;
//下边的边
while(c>=j)
{
if(A[r][c--]==0) goto l3;
}
c++;
//左边的边
while(r>=i)
{
if(A[r--][c]==0) goto l3;
}
//r++;--------- 无需恢复
return n;
}
}
n--;
}
return 0;
}
int main()
{
vector<vector<int> > arr;
int row=5,col=5;
//二维向量赋初值
int i=0,j=0;
//赋初值
vector<int> temp;
//第一行
temp.push_back(0);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
arr.push_back(temp);
temp.clear();
//第二行
temp.push_back(0);
temp.push_back(1);
temp.push_back(0);
temp.push_back(1);
temp.push_back(0);
arr.push_back(temp);
temp.clear();
//第三行
temp.push_back(0);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
arr.push_back(temp);
temp.clear();
//第四行
temp.push_back(0);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
arr.push_back(temp);
temp.clear();
//第五行
temp.push_back(0);
temp.push_back(1);
temp.push_back(0);
temp.push_back(1);
temp.push_back(1);
arr.push_back(temp);
temp.clear();
//打印二维向量
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
cout<<arr[i][j]<<" ";
}
cout<<endl;
}
cout<<endl;
cout<<solve(arr);
return 0;
}
continue:结束本次循环,不执行循环体内continue下面的语句,继续下次循环。
break:终止整个循环体,无论多少层。
goto:与标志联用,容易造成程序的混乱。
结果:
2、边界为1的最大子方阵优化:
①、可以通过对原有矩阵进行预处理的方式,这个思路类似于动态规划中的打表法。
②、我们可以构建一个三维数组,二维平面空间和矩阵大小相同,第三维存储两个元素,第一个元素存储包括自己以及自
己右方连续的1的个数,如果自己为0,那么自己就存储为0。第二个元素存储包括自己以及自己下方连续的1的个数,如果
自己为0,那么自己也存储为0。
③、通过生成这个辅助表,就可以在判断是否是最大方阵的时候加快判断速度,可以将4个while循环去除,而通过这个辅助
表只需判断一次即可,时间复杂度为O(1),那么整个算法的时间复杂度就为N*N*N了,为O(N3)。
假如矩阵为:
-----------------------------
{ 1, 1, 0, 1 }
{ 1, 1, 1, 1 }
{ 1, 1, 0, 1 }
{ 1, 1, 1, 1 }
-----------------------------
生成的辅助数组为:
-----------------------------
2,4 1,4 0,0 1,4
4,3 3,3 2,1 1,3
2,2 1,2 0,0 1,2
4,1 3,1 2,1 1,1
-----------------------------
代码:
public class MaxSquare_1 {
public static void main(String[] args) {
// int[][] A = {
// { 0, 1, 1, 1, 1 },
// { 0, 1, 0, 1, 0 },
// { 0, 1, 1, 1, 1 },
// { 0, 1, 1, 1, 1 },
// { 0, 1, 0, 1, 1 }
// };
int [][]A = new int[][] {
{ 1, 1, 0, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 0, 1 },
{ 1, 1, 1, 1 },
};
generateHelpRec(A);
System.out.println("生成的辅助数组为:");
System.out.println("=============================");
print(rec, A.length);
System.out.println("=============================");
System.out.println("最大子方阵的边长为:"+solve(A));
}
private static void print(int[][][] rec, int N) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(rec[i][j][0] + "," + rec[i][j][1] + "\t");
}
System.out.println();
}
}
static int [][][]rec;
/**
* 记录每个元素往右和往下有多少个连续的1
* @param A
*/
private static void generateHelpRec(int[][] A) {
int N = A.length;
rec = new int[N][N][2];
int row = N-1;
// 初始化最后一行,因为记录的是往右和往下的,所以必须从最后一行向上初始化,而且从右向左初始化
for(int j=N-1;j>=0;j--){
int value = A[row][j];
if (value==1) {
if (j==N-1) { // 避免数组越界
rec[row][j][0] = 1; // 右边界
}else {
// A的元素值为1,rec在这个位置的连续1的个数 = 右边位置的连续的1的个数+1
rec[row][j][0] = rec[row][j+1][0] + 1;
}
// 最后一行的下方的1的连续数==1
rec[row][j][1] = 1;
}
}
row--; //开始初始化倒数第二行到第一行
for(int i = row;i>=0;i--){
for (int j = N-1; j>=0; j--) {
int value = A[i][j];
// 利用右边和下边已经生产的数据来推出现在这个位置上右侧和下方有多少个1
if(value==1){
if (j==N-1) {
rec[i][j][0] = 1;// 右侧连续1的个数
}else {
rec[i][j][0] = rec[i][j+1][0]+1;
}
rec[i][j][1] = rec[i+1][j][1] + 1; // 向下连续1的个数
}
}
}
}
// O(n3)
static int solve(int [][]A){
int N = A.length;
int n = N;
while(n>0){
for (int i = 0; i < N; i++) {
if (i+n>N) break;
l3: // 这个语言相当于给第二层for循环命令 下面代码中的continue继续的是第二层的for循环,不是while循环
for (int j = 0; j < N; j++) {
if (j+n>N) break;
if (check(i, j, n))
return n;
}
}
n--;
}
return 0;
}
// O(1)
private static boolean check(int i, int j, int n) {
// 左上角那个点往右数的1的数目要大于等于n
// 左上角那个点往下数的1的数目要大于等于n
// 右上角那个点往下数的1的个数要大于等于n
// 左下角那个点往右数的1的个数要大于等于n
if (rec[i][j][0]>=n&&rec[i][j][1]>=n&&rec[i][j+n-1][1]>=n&&rec[i+n-1][j][0]>=n) {
return true;
}
return false;
}
}
结果:
小结:
计算优化过后的时间复杂度为: 生成辅助数组所花时间N2+后面循环遍历时间N3,加起来整个算法的时间复杂度就
为O(N3)了,那这个算法的效率就比优化前的算法的效率好太多了。