2020年3月25日華爲春招真題第2題詳解+Java代碼實現——最大正方形子矩陣(校招,代碼,原理解釋,動態規劃)

一、問題背景

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章