數字之魅:子陣列之和的最大值[二維]+[三維]
題目:如何求出一個二維陣列中的最大子陣列之和。
方案一:暴力破解-列舉法。對於一個二維陣列我們列舉出每一個子陣列值的大小,然後進行比較,這樣就可以得到最大的和了。其時間複雜度為:O(N*N*M*M*Sum的時間複雜度)[N表示行數,M表示列數,Sum是求解子矩陣的和]。由於Sum函式求和也是採用迴圈,足見這個時間複雜度可是相當的大。
方案二:先計算出以左上角的元素(1,1)和當前元素(i,j)為頂點對的子矩陣的部分和,部分和的計算如下圖(一)所示,我們可以看出:以(i_min,j_min),(i_min,j_max),(i_max,j_min),(i_max,j_max)為頂點的矩形區域的元素和為:Sum[i_max][j_max] = Sum[i_max][j_max]-Sum[i_min-1][j_max]-Sum[i_max][j_min-1]+Sum[i_min-1][j_min-1]。也就是在已知部分和的基礎上就可以在0(1)的時間內得到任意矩形的元素之和。
那麼如何得到部分和呢?
如圖二所示,在更小的“部分和”基礎上,也能以O(1)得到新的“部分和”。公式為:Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+arr[i][j] 這裡的arr[i][j]是矩陣中的arr[i][j]的值。因此在這裡我們可以用O(N*M)來得到部分和。這裡我們主要去掉了計演算法子矩陣和的時間複雜度,所以其時間複雜度為:O(N*N*M*M).相比與方案一有所提高。
擴充套件問題1:如果二維陣列也是首尾相連,像一條首尾相連的帶子,演算法會如何改變?
其實現程式碼如下:
長方體我們需要求子長方體的最大值,那麼可以想象一下!其實思路和二維是一樣的,主要是採取降維的思路,這樣可以把複雜的問題簡單化。主要關鍵還是在求部分和,其求解公式如下:
PS[i][j][k] = A[i][j][k]+PS[i-1][j][k]+PS[i][j-1][k]+PS[i][j][k-1]-PS[i-1][j-1][k]-PS[i-1][j][k-1]-PS[i][j-1][k-1]+PS[i-1][j-1][k-1]; [i,j,k形象點兒可分別代表其長寬高]
其時間複雜度為:O(N*N*M*M*Q)=O(N^5)。可以得出:一維是O(N),二維是O(N*N),三維是O(N^5).
具體實現程式碼如下:
方案一:暴力破解-列舉法。對於一個二維陣列我們列舉出每一個子陣列值的大小,然後進行比較,這樣就可以得到最大的和了。其時間複雜度為:O(N*N*M*M*Sum的時間複雜度)[N表示行數,M表示列數,Sum是求解子矩陣的和]。由於Sum函式求和也是採用迴圈,足見這個時間複雜度可是相當的大。
方案二:先計算出以左上角的元素(1,1)和當前元素(i,j)為頂點對的子矩陣的部分和,部分和的計算如下圖(一)所示,我們可以看出:以(i_min,j_min),(i_min,j_max),(i_max,j_min),(i_max,j_max)為頂點的矩形區域的元素和為:Sum[i_max][j_max] = Sum[i_max][j_max]-Sum[i_min-1][j_max]-Sum[i_max][j_min-1]+Sum[i_min-1][j_min-1]。也就是在已知部分和的基礎上就可以在0(1)的時間內得到任意矩形的元素之和。
那麼如何得到部分和呢?
如圖二所示,在更小的“部分和”基礎上,也能以O(1)得到新的“部分和”。公式為:Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+arr[i][j] 這裡的arr[i][j]是矩陣中的arr[i][j]的值。因此在這裡我們可以用O(N*M)來得到部分和。這裡我們主要去掉了計演算法子矩陣和的時間複雜度,所以其時間複雜度為:O(N*N*M*M).相比與方案一有所提高。
圖一 圖二
方案三:其實我們仔細的想一想,就可以發現,它的最簡單的原型是求一維陣列中的最大值。那麼基於這個啟發,我們可以把問題從二維轉化為一維以提高演算法效能。假設已經確定了矩陣區域的上下邊界,知道矩陣區域的上下邊界分佈是第a行和第c行,就把每列的第a行和第c行之間的元素看成一個整體。即求陣列:(merge[1],merge[2],merge[3]....).merge[i]=arr[a][i]+arr[b][i]+..+arr[c][i].
這樣我們可以列舉矩形的上下邊界,然後再用一維情況下的方法確定左右邊界,相當於一維陣列中的一個元素(通過子矩陣部分和可以在O(1)時間內計算出整體之和),就可以得到二維問題的解。新方法的時間複雜度為:O(N*N*M)=O(N*N*N)。具體過程如下圖三所示:
圖三
方案一的具體實現程式碼:int arr[N][M];
int Max(int x,int y)//得到最大值;
{
return (x>y)?x:y;
}
int Sum(int imin,int imax,int jmin,int jmax)//得到子矩陣的和,時間複雜度為O(N*N);
{
int sum=0;
for(int i=imin;i<=imax;i++)
for(int j=jmin;j<jmax;j++)
sum+=arr[i][j];
return sum;
}
int MaxSum(int *arr,int m,int n) //得到子矩陣的最大值;
{
int maximum=0;
for(int i_min=1;i_min<=n;i_min++)
for(int i_max=i_min;i_max<=n;i_max++)
for(int j_min=1;j_min<=m;j_max++)
for(int j_max=j_min;j_max<=m;j_max++)
maximum=Max(maximum,Sum(i_min,i_max,j_min,j_max));
return maximum;
}
方案二的具體實現程式碼:int arr[N][M];
int Sum[N][M];//儲存部分和的陣列;
int Max(int x,int y)//得到最大值;
{
return (x>y) ? x:y;
}
void GetSum()//預處理得到部分和;
{
for(int i=0;i<=n;i++)
Sum[i][0]=0;//邊界值;
for(int j=0;j<=n;j++)
Sum[0][j]=0;//邊界值;真正的部分和資料是從Sum[1][1]開始的;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+arr[i][j]
}
inline int Sum(int i_min,int i_max,int j_min,int j_max)//呼叫比較頻繁,可設定為行內函數,提高效率。和方案一相比,其時間複雜度為O(1).
{
return Sum[i_max][j_max]-Sum[i_min-1][j_max]-Sum[i_max][j_min-1]+Sum[i_min-1][j_min-1];
}
int MaxSum(int *arr,int m,int n)
{
int maximum=0;
for(int i_min=1;i_min<=n;i_min++)
for(int i_max=i_min;i_max<=n;i_max++)
for(int j_min=1;j_min<=m;j_max++)
for(int j_max=j_min;j_max<=m;j_max++)
maximum=Max(maximum,Sum(i_min,i_max,j_min,j_max));
return maximum;
}
方案三的具體實現程式碼:#include <iostream>
#include <algorithm>
using namespace std;
#define LEN 888
int arr[LEN][LEN];
long long Sum[LEN][LEN];
inline long long MatrixSum(int s, int t, int i, int j)
{
return Sum[i][j]-Sum[i][t-1]-Sum[s-1][j]+Sum[s-1][t-1];
}
int main()
{
int row, col, i, j;
cout<<"please input the row and col of the array:"<<endl;
cin >> row >> col;
cout<<"please input the data of the array:"<<endl;
for (i=1; i<=row; i++) //輸入資料;
for (j=1; j<=col; j++)
cin >> arr[i][j];
for (i=0; i<=row; i++) //設定邊界線;
Sum[i][0] = 0;
for (j=0; j<=col; j++)
Sum[0][j] = 0;
for (i=1; i<=row; i++) // 預處理計算矩陣的部分和
for (j=1; j<=col; j++)
Sum[i][j] = arr[i][j]+Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1];
int a, c;
long long MaxSum = arr[1][1]; //初始值的設定;
for (a=1; a<=row; a++)
for (c=a; c<=col; c++) // 將子矩陣上下邊界設為第a行和第c行,求取取最大值
{
long long Tail = MatrixSum(a, 1, c, 1); //合併列;
for (j=2; j<=col; j++) //按一維陣列向後列舉;
{
Tail = max( MatrixSum(a, j, c, j), MatrixSum(a, j, c, j)+Tail);
MaxSum = max(Tail, MaxSum);
}
}
cout <<"最大的子矩陣和為:"<< MaxSum<<endl;
system("pause");
return 0;
}
執行結果如下:擴充套件問題1:如果二維陣列也是首尾相連,像一條首尾相連的帶子,演算法會如何改變?
其實現程式碼如下:
#include <iostream>
#include <algorithm>
using namespace std;
#define LEN 1003
int arr[LEN][LEN];
long long Sum[LEN][LEN];
inline long long MatrixSum(int s, int t, int i, int j)
{
return Sum[i][j]-Sum[i][t-1]-Sum[s-1][j]+Sum[s-1][t-1];
}
int main()
{
int row, col, i, j;
cout<<"please input the row and col of the array:"<<endl;
cin >> row >> col;
cout<<"please input the data of the array:"<<endl;
for (i=1; i<=row; i++)
for (j=1; j<=col; j++)
cin >> arr[i][j];
for (i=0; i<=row; i++)
Sum[i][0] = 0;
for (j=0; j<=col; j++)
Sum[0][j] = 0;
// 計算矩陣的部分和
for (i=1; i<=row; i++) //計算部分和;
for (j=1; j<=col; j++)
Sum[i][j] = arr[i][j]+Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1];
int a, c;
long long MaxSum = arr[1][1]; // 上下邊界不會跨過第n行和第1行
for (a=1; a<=row; a++)
for (c=a; c<=row; c++)
{
// 將子矩陣上下邊界設為第a行和第c行
// 左右邊界不會跨過第m列和第1列
long long Tail = MatrixSum(a, 1, c, 1);
for (j=2; j<=col; j++)
{
Tail = max(MatrixSum(a, j, c, j),
MatrixSum(a, j, c, j)+Tail);
MaxSum = max(Tail, MaxSum);
}
long long Sum = MatrixSum(a, 1, c, 1); // 左右邊界會跨過第n列和第1列
long long Start = Sum;
int sind = 1;
for (i=2; i<=col; i++)
{
Sum += MatrixSum(a, i, c, i);
if (Sum > Start) {Start = Sum; sind = i;}
}
Tail = MatrixSum(a, col, c, col);
int tind = col;
for (j=col-1; j>=1; j--)
{
Sum += MatrixSum(a, j, c, j);
if (Sum > Tail) {Tail = Sum; tind = j;}
}
if (sind<tind && Start+Tail>MaxSum)
MaxSum = Start+Tail;
}
cout <<"最大的子矩陣和為:"<< MaxSum<<endl;
system("pause");
return 0;
}
擴充套件問題2:求3維矩陣,也就是長方體中子長方體的最大值?長方體我們需要求子長方體的最大值,那麼可以想象一下!其實思路和二維是一樣的,主要是採取降維的思路,這樣可以把複雜的問題簡單化。主要關鍵還是在求部分和,其求解公式如下:
PS[i][j][k] = A[i][j][k]+PS[i-1][j][k]+PS[i][j-1][k]+PS[i][j][k-1]-PS[i-1][j-1][k]-PS[i-1][j][k-1]-PS[i][j-1][k-1]+PS[i-1][j-1][k-1]; [i,j,k形象點兒可分別代表其長寬高]
其時間複雜度為:O(N*N*M*M*Q)=O(N^5)。可以得出:一維是O(N),二維是O(N*N),三維是O(N^5).
具體實現程式碼如下:
#include <iostream>
#include <algorithm>
using namespace std;
#define LEN 500
int arr[LEN][LEN][LEN];
int Sum[LEN][LEN][LEN];
inline int MaxCubeSum(int a, int b, int c, int d, int i, int j)
{
return Sum[b][d][j]-Sum[a-1][d][j]-Sum[b][c-1][j]-Sum[b][d][i-1]+
Sum[a-1][c-1][j]+Sum[a-1][d][i-1]+Sum[b][c-1][i-1]-Sum[a-1][c-1][i-1];
}
int main()
{
int row, col, high, i, j, k;
cout<<"please input the row, col and high of the array:"<<endl;
cin >> row >> col >>high;
cout<<"please input the data of the array:"<<endl;
for (i=1; i<=row; i++)
for (j=1; j<=col; j++)
for (k=1; k<=high; k++)
cin >> arr[i][j][k];
for (i=0; i<=row; i++)
for (j=0; j<=col; j++)
Sum[i][j][0] = 0;
for (i=0; i<=row; i++)
for (k=0; k<=high; k++)
Sum[i][0][k] = 0;
for (j=0; j<=col ; j++)
for (k=0; k<=high; k++)
Sum[0][j][k] = 0;
// 計算長方體的部分和
for (i=1; i<=row; i++)
for (j=1; j<=col; j++)
for (k=1; k<=high; k++)
Sum[i][j][k] = arr[i][j][k]+Sum[i-1][j][k]+Sum[i][j-1][k]+Sum[i][j][k-1]-Sum[i-1][j-1][k]-Sum[i-1][j][k-1]-Sum[i][j-1][k-1]+Sum[i-1][j-1][k-1];
int a, b, c, d;
int MaxSum = arr[1][1][1];
// 限制第一維的取值範圍
for (a=1; a<=row; a++)
for (b=a; b<=row; b++)
// 限制第二維的取值範圍
for (c=1; c<=col; c++)
for (d=c; d<=col; d++)
{
// 只剩下最後一維沒有確定,利用一維部分和的方法
int Tail = MaxCubeSum(a,b,c,d,1,1);
for (j=2; j<=k; j++)
{
int cur = MaxCubeSum(a,b,c,d,j,j);
Tail = max(Tail+cur, cur);
MaxSum = max(Tail, MaxSum);
}
}
cout <<"最大的子矩陣和為:"<< MaxSum<<endl;
system("pause");
return 0;
}
相關文章
- 數字之魅:尋找陣列中的最大值和最小值陣列
- JavaSE 陣列:一維陣列&二維陣列Java陣列
- 修改陣列【並查集維護集合的最大值、連續數字的最大值】陣列並查集
- 演算法之陣列——三數之和演算法陣列
- js 一維陣列轉二維陣列JS陣列
- js 二維陣列轉一維陣列JS陣列
- PHP二維陣列轉一維陣列PHP陣列
- 二維陣列陣列
- java之陣列的索引,排序以及二維陣列Java陣列索引排序
- PHP中二維陣列與多維陣列PHP陣列
- JavaScript --二維陣列查詢一維陣列JavaScript陣列
- C/C++ 二維陣列的理解(多維陣列)C++陣列
- PHP把MYSQL重複ID二維陣列重組為三維陣列PHPMySql陣列
- JavaScript二維陣列轉換成一維陣列JavaScript陣列
- Java 二維陣列轉一維Java陣列
- vector 二維陣列陣列
- Java二維陣列Java陣列
- JavaScript二維陣列JavaScript陣列
- JavaScript 二維陣列JavaScript陣列
- 二維陣列排序陣列排序
- Rust與Java程式碼比較:將二維陣列轉為三維陣列RustJava陣列
- 用三列二維陣列表示的稀疏矩陣類陣列矩陣
- 二維陣列的計算陣列
- 二維陣列的獲取陣列
- C++動態建立二維陣列,二維陣列指標,以及動態二維陣列函式傳遞C++陣列指標函式
- 一維子陣列最大和陣列
- PHP一維陣列轉二維陣列正規表示式PHP陣列
- 二維陣列查詢陣列
- php 二維陣列排序PHP陣列排序
- 二維陣列行排序陣列排序
- perl操作二維陣列陣列
- 二維陣列傳參陣列
- 二維樹狀陣列陣列
- 二維陣列練習陣列
- c++ -- 二維陣列引數傳遞C++陣列
- 最長公共子串 二維陣列 Go實現陣列Go
- 二維陣列和稀疏陣列互轉陣列
- 二維陣列中的查詢陣列