參考來源:
https://blog.csdn.net/fengyingjie2/article/details/54427146
https://blog.csdn.net/kavu1/article/details/50547401
題目:
給定一個矩陣,都是整數,其中(n≤500),求出其中的最大子矩陣
解題思路:
O(n^2*n^2*n^2)的做法:
很容易想到,可以枚舉一個矩陣的左上角和右下角的坐標確定這個矩陣,然后,再用兩個循環進行統計
O(n^2*n^2)的做法:
對於上面的方法,提前處理一個前綴和,即可省去后面統計的時間復雜度。
正解,即O(n³)事實上並沒有這么多:
想要做對這道題,我們先要明白一個最大子段和算法的做法:
例如數字a={8,-2,3,-11,5},我們需要求出一段連續數字的最大和是什么
我們的思想是DP
設f[i]表示從1~i這些位置中的最大子段和
如何轉移?對於當前的點i,我們只有兩種狀態,選擇與上一個子段和連接或者自己重新構成一個子段和。
若選,則與1~i-1的最大子段和有關系,即1~i-1的最長子段和加上當前的a[i],重新構成一個子段和
若不選,則與1~i-1最大子段和沒有關系,自己重新構成了一個子段和
兩者取較大者,即可解決
列出狀態轉移方程:f[i]:=max(a[i],f[i-1]+a[i])
時間復雜度 O(n)
說了這么多都是鋪墊,下面是重點:
為了能夠在原始矩陣里很快得到從 i 行到 j 行 的上下值之和,我們這里用到了一個輔助矩陣,它是原始矩陣從上到下加下來的。
假設原始矩陣是matrix, 它每一層上下相加后得到的矩陣是total,那么我們可以通過如下代碼實現:
int[][] total = matrix; for (int i = 1; i < matrix[0].length; i++) { for (int j = 0; j < matrix.length; j++) { total[i][j] += total[i-1][j]; } }
如果我們要求第 i 行到第 j 行之間上下值的和,我們可以通過total[j][k] - total[i-1][k] 得到, k 的范圍從1 到 matrix[0].length - 1。
有了這些知識點,我們只需要在所有的情況下,把它們所對應的局部最大子矩陣進行比較,就可以得到全局最大的子矩陣。代碼如下:
1 public int subMaxMatrix(int[][] matrix) { 2 3 int[][] total = matrix; 4 for (int i = 1; i < matrix[0].length; i++) { 5 for (int j = 0; j < matrix.length; j++) { 6 total[i][j] += total[i-1][j]; 7 } 8 } 9 10 int maximum = Integer.MIN_VALUE; 11 for (int i = 0; i < matrix.length; i++) { 12 for (int j = i; j < matrix.length; j++) { 13 //result 保存的是從 i 行 到第 j 行 所對應的矩陣上下值的和 14 int[] result = new int[matrix[0].length]; 15 for (int f = 0; f < matrix[0].length; f++) { 16 if (i == 0) { 17 result[f] = total[j][f]; 18 } else { 19 result[f] = total[j][f] - total[i - 1][f]; 20 } 21 } 22 int maximal = maxSubsequence(result); 23 24 if (maximal > maximum) { 25 maximum = maximal; 26 } 27 } 28 } 29 30 return maximum; 31 }