一、問題背景
博主最近在準備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 華爲筆試題