日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

计算矩阵中全1子矩阵的个数

發布時間:2024/8/23 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 计算矩阵中全1子矩阵的个数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

最近被我大哥安利了一道算法題, 這道題說難, 還不至于我做不出來, 說簡單吧, 我還想不到最優解, 等把最優解告訴我之后, 我還正好能理解. 我甚至曾經怯怯的認為, 這題就是我哥專門給我找的, 嘿嘿, 心中說不出的小歡喜.

題來了, 此題出自力扣, 原題鏈接:

https://leetcode-cn.com/problems/count-submatrices-with-all-ones/

描述: 給你一個只包含 0 和 1 的?rows * columns?矩陣?mat?,請你返回有多少個?子矩形?的元素全部都是 1 。

例子:

輸入:mat = [[1,0,1],[1,1,0],[1,1,0]] 輸出:13 解釋: 有 6?個 1x1 的矩形。 有 2 個 1x2 的矩形。 有 3 個 2x1 的矩形。 有 1 個 2x2 的矩形。 有 1 個 3x1 的矩形。 矩形數目總共 = 6 + 2 + 3 + 1 + 1 = 13?。

題意清晰明了, 開始嘗試解題(使用 C 來進行解題).

方案一

首先直觀上最先想到的, 就是窮舉了. 一力破十會. 將所有出現的情況遍歷一遍, 然后就能得出總數了. 思路如下:

  • 利用i,?j?將二維數組的所有節點遍歷一遍
  • 利用m,?n將以[i][j]為左上頂點的子矩陣遍歷一遍
  • 判斷i,?j,?m,?n四個變量確定的矩陣是否為全1矩陣
  • 代碼實現:

    int numSubmat(int** mat, int matSize, int* matColSize){int result = 0;// 遍歷所有節點for (int i = 0; i < matSize; i++) {for (int j = 0; j < *matColSize; j++) {// 遍歷當前節點為左上頂點的所有子矩陣for (int m = i; m < matSize; m++) {for (int n = j; n < *matColSize; n++) {// 判斷當前子矩陣是否為全1矩陣int isOk = 1;for (int p = i; p <= m; p++) {for (int q = j; q <= n; q++) {if(mat[p][q] != 1){isOk = 0;break;}}if(!isOk) break;}// 計算總數if(isOk) result++;}}}}return result; }

    隨手寫個測試用:

    #include <stdio.h> #include <stdlib.h>int numSubmat(int** mat, int matSize, int* matColSize);int main() {// 定義數組長度int matSize = 3, matColSize = 3;// 分配數組空間int **mat = (int **)malloc(matSize*sizeof(int*));// 動態分配內容for (int i = 0; i < matSize; i++) {mat[i] = (int *)malloc(matColSize*sizeof(int));}// 給數組填內容, 這里可以接收鍵盤數組mat[0][0] = 1;mat[0][1] = 0;mat[0][2] = 1;mat[1][0] = 1;mat[1][1] = 1;mat[1][2] = 0;mat[2][0] = 1;mat[2][1] = 1;mat[2][2] = 0;int result = numSubmat(mat, matSize, &matColSize);printf("%d", result);return 0; }

    執行過后, OK, 么的問題. 看一下時間復雜度呢? 一眼就看到了函數里的六層循環, 么的說, O(n^6).

    這時, 我大哥說他的時間復雜度是 O(n^3). 那我這小心情, 必須整出來, 再想.

    方案二

    上面的六層循環中, 能不能想辦法去掉一層呢? 有. 在最后判斷是否全1的循環中, 如果左上的數字是0, 那必然沒有全1子矩陣了

    再如果向下找的時候, 碰到0, 那下一列的時候也沒必要超過這里了, 因為子矩陣至少有一個0了, 如下圖:

    在向右遍歷的時候同理, 這樣, 我們就可以確定, 所有遍歷到的值都是1, 可以將判斷全1的兩層循環去掉. nice. 修改代碼如下:

    int numSubmat(int** mat, int matSize, int* matColSize){int result = 0;// 遍歷所有節點for (int i = 0; i < matSize; i++) {for (int j = 0; j < *matColSize; j++) {if(mat[i][j] == 0) continue;int thisMaxColSize = *matColSize; // 當前向右最大值// 遍歷當前節點為左上頂點的所有子矩陣for (int m = i; m < matSize; m++) {for (int n = j; n < thisMaxColSize; n++) {if(mat[m][n] == 1) result++;// 記錄向右的最大值else thisMaxColSize = n;}}}}return result; }

    OK, 經過測試完全么的問題. 再看看現在的時間復雜度. O(n^4); 比剛才的六次方, 直接降了兩個數量級. 但是比我大哥還差點意思哈.

    方案三

    打擾了, 沒有想到O(n^3)的解法. 經過我哥的一番指點, 可以說是豁然開朗. 思路不變. 上面的四層循環, 有沒有什么辦法能再減少一層呢?

    想一下, 我們在第四層循環中, 向右遍歷, 找的是什么? 是連續1的個數, 如果我們不用向右遍歷, 直接就知道了這個連續1的個數, 那是不是就可以把這一層也省了呢?

    那么問題來了, 如何不遍歷就知道呢??預處理. 在所有的遍歷之前, 先進行一次遍歷, 把每個節點向右的連續1個數計算好. 這個思路有點妙啊. 廢話不多說, 再來:

    int min(int a, int b){return a > b ? b : a; }int numSubmat(int** mat, int matSize, int* matColSize){// 進行預處理, 將每個節點向右的連續1個數算好(從右下向左上處理)for (int i = matSize - 1; i >= 0; i--) {for (int j = *matColSize - 1; j >= 0 ; j--) {if(mat[i][j] == 0) continue;// 最右側不處理if(j == *matColSize-1) continue;// 每個節點的數字等于右邊的加1mat[i][j] = mat[i][j+1] + 1;}}int result = 0;// 遍歷所有節點for (int i = 0; i < matSize; i++) {for (int j = 0; j < *matColSize; j++) {if(mat[i][j] == 0) continue;int thisMaxColSize = *matColSize; // 當前向右最大值// 遍歷當前節點為左上頂點的所有子矩陣for (int m = i; m < matSize; m++) {// 記錄向右的最大值thisMaxColSize = min(thisMaxColSize, mat[m][j]);result += thisMaxColSize;}}}return result; }

    再看時間復雜度, 終于, O(n^3).


    還有沒有比三次方更快的解法呢? 可能..大概..或許有吧. 但是我想了好久也沒有想到.

    以上, 其實到第二個方案我都想到了, 但是最后一步怎么都沒邁出去, 原因歸結為做的少, 遇到的少. 算法題偶爾做做還挺好的, 也不需要很高深的數學知識, 還可以鍛煉思維, 蠻有趣的, 之后可以抽時間來看看, 嘿嘿.

    總結

    以上是生活随笔為你收集整理的计算矩阵中全1子矩阵的个数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。