一、问题背景
博主最近在准备2020年的软件工程编程领域的春招,其中华为提供了博主上机考试的机会,因此博主参加了2020年3月25日的华为校园春招。其中博主李同学做出了2020年3月25日华为校园春招真题的第1题和第2题。第2题的题目描述和Java代码实现如下文所示,但第1题的题目描述和Java代码实现请见博主的此篇博客2020年3月25日华为春招真题第1题详解+Java代码实现——2进制与10进制的相互转换。
二、2020年3月25日华为校园春招真题的第2题(最大正方形子矩阵)
1.题目描述
题目描述:输入的第一行是一个数字n,n代表一个矩阵的行数;输入的第2到第n+1行代表一个矩阵的每一行的元素,元素只能是0或1,且每行长度相同。请输出该矩阵中元素全为1的最大正方形子矩阵的面积。输入的矩阵可能是单行矩阵和单列矩阵。
若输入:
3
110
111
110上述输入对应的输出为:
4
若输入:
8
1010111111
0000000111
1010110111
0000111111
1010111111
0000001111
1010111111
0000110001上述输入对应的输出为:
16
2.Java代码实现
2.1暴力代码实现(时间复杂度O(n4))
以下代码是博主李同学在考试时慌忙写下的暴力代码实现,尽管写了flag做了优化,但此暴力代码的最坏时间复杂度可达O(n4),因此这种做法是不太可取的,于是博主参考了网上其他大神的做法,即用动态规划可以在时间复杂度为O(n2)的情况下实现题目要求,请看下文的2.2动态规划代码实现。
import java.util.Scanner;
public class Problem2 {
public static void main(String[] args) {
//正方形最大的长度值
int maxLength=0;
//先获得输入数据
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
//用n行字符串存储该矩阵
String[] strings=new String[n];
for (int i = 0; i < n; i++) {
strings[i]=scanner.next();
}
//首先判断该矩阵是否是单行或单列矩阵
//当n为1时,该矩阵为单行矩阵
if (n==1){
//若单行矩阵中还包含元素1则输出最大面积1,否则输出最大面积0
if (strings[0].contains("1")){
System.out.print(1);
return;
}else {
System.out.print(0);
return;
}
}
//若矩阵第一行的长度就是1的话,代表该矩阵是单列矩阵
else if (strings[0].length()==1){
//若单列矩阵中还包含元素1则输出最大面积1,否则输出最大面积0
for (int i = 0; i < n; i++) {
if (strings[i].contains("1")){
System.out.print(1);
return;
}
}
System.out.println(0);
}
//若该矩阵不是单行或单列矩阵,则继续进行一下判断
else {
//再找到正方形左上角为1的元素作为查找起点
for (int i = 0; i < n; i++) {
for (int j = 0; j < strings[0].length(); j++) {
//若某元素是1,则该元素可作为正方形的左上角
if (strings[i].charAt(j)=='1'){
//根据左上角坐标[i][j]判断,该正方形最多可延伸的长度
int maxStretch=Math.min(n-1-i,strings[0].length()-1-j);
//那么[i+maxStretch][j+maxStretch]是最多可延伸的正方形的右下角坐标点
for (int k = 1; k <= maxStretch; k++) {
//若延长后的正方形的右下角坐标值是1,才有讨论该矩阵元素是否全是1的余地
if (strings[i+k].charAt(j+k)=='1'){
//System.out.println((i+k)+" "+(j+k));
//标志该正方形矩阵中是否有0的flag,若flag是0则代表该矩阵中有0
int flag=1;
for (int l = 0; l <= k; l++) {
//若该行字符串包含一个0则直接结束
if (strings[i+l].substring(j,j+k+1).contains("0")){
flag=0;
break;
}
}
//矩阵中没有一个0,那么maxLength
if (flag==1){
maxLength=Math.max(maxLength,k+1);
}
}
//如果这个正方形的右下角元素为0,那么之后的延展矩阵肯定不会全是1的,所以这里break;直接跳过
else {
break;
}
}
}
}
}
System.out.print(maxLength*maxLength);
}
}
}
2.2动态规划代码实现(时间复杂度O(n2))
import java.util.Scanner;
public class Problem2 {
public static void main(String[] args) {
//正方形最大的长度值
int maxLength=0;
//先获得输入数据
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
//用n行字符串存储该矩阵
String[] strings=new String[n];
for (int i = 0; i < n; i++) {
strings[i]=scanner.next();
}
//建议一个n行strings[0].length()列的二维int数组存储元素
int[][] dp=new int[n][strings[0].length()];
//初始化dp的第0列
for (int i = 0; i < n; i++) {
//-'0'是因为charAt(0)生成的是char型'0'到'9',它们的Int值是48-58
dp[i][0]=strings[i].charAt(0)-'0';
//若该动态规划的元素值是1代表有一个长度为1的正方形
if (dp[i][0]==1){
maxLength=1;
}
}
//初始化dp的第0行
for (int i = 0; i < strings[0].length(); i++) {
//-'0'是因为charAt(0)生成的是char型'0'到'9',它们的Int值是48-58
dp[0][i]=strings[0].charAt(i)-'0';
//若该动态规划的元素值是1代表有一个长度为1的正方形
if (dp[0][i]==1){
maxLength=1;
}
}
//从dp的第1行第1列开始,从左往右从上至下计算该dp元素的值,解出dp
for (int i = 1; i < n; i++) {
for (int j = 1; j < strings[0].length(); j++) {
//在原来的二维数组中,若该元素值是1,那么计算该位置的dp值,否则该位置的dp值设为0(dp数组初始化时所有元素为0,所以下面没有设值为0的代码)
if (strings[i].charAt(j)=='1'){
dp[i][j]=Math.min(Math.min(dp[i-1][j-1],dp[i][j-1]),dp[i-1][j])+1;
maxLength=maxLength>dp[i][j]?maxLength:dp[i][j];
}
}
}
System.out.println(maxLength*maxLength);
}
}
本文参考文献:
[1]Tsai笔记:C++学习随性笔记(5)—— 华为面试题目总结 [1]华为笔试题:根据子网掩码判断两个IP地址是否在同一子网,并输出IP1的网络号 [3]2020年3月25日华为实习生机试编程题 [4]华为机试整理3.25 [5]2020/3/25 华为笔试题