題目鏈接:
http://acm.nyist.net/JudgeOnline/problem.php?pid=104
描述
給定一個由整數組成二維矩陣(r*c),現在需要找出它的一個子矩陣,使得這個子矩陣內的所有元素之和最大,並把這個子矩陣稱爲最大子矩陣。
例子:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其最大子矩陣爲:
9 2
-4 1
-1 8
其元素總和爲15。
輸入
第一行輸入一個整數n(0<n≤100),表示有n組測試數據;
每組測試數據:
第一行有兩個的整數r,c(0<r,c≤100),r、c分別代表矩陣的行和列;
隨後有r行,每行有c個整數;
輸出
輸出矩陣的最大子矩陣的元素之和。
樣例輸入
1 4 4 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2 |
樣例輸出
15 |
算法思想:
算法思想:考慮求一維的數組連續子序列和最大值,算法很簡單,典型的動態規劃,使用max_sum表示從第一數到目前的最大和,sum是某段區間的和,如果sum<0,那麼sum沒有利用價值了,把sum=0,否則就繼續往上加。
max_sum=max((sum+=a[i])<0?0:sum,max_sum);
二維的問題,其實可以轉化爲一維的問題。
這個轉化需要一個技巧,如果不使用這個技巧來做會稍微複雜些。二維問題其實就相當於多個一維問題的求解。
首先我們要注意到二維子矩陣在選取的時候是個矩陣,聯繫一個我們經常會用到的技巧:但我們需要頻繁計算一個數據任意一個區間
的和的時候,我們會預先把這個數組使用啊a[i]=a[i]+a[i-1]的方式把它記錄的值變爲數組到這個位置的和,這樣的好處就是任意一個區間[i,j]的和就可轉化爲了a[j]-a[i-1]。
在這裏我們依然採用這樣的技巧。我們把這個矩陣記錄的值對於每個列向量都做上述改變。
然後我們就發現,但我們選取任意的連續行進行組合的時候,這個行區間對於的列的值的和都可以用上述方法快速獲得,那麼對於每個列的和又會變爲一個求一維連續區間最大和問題了。到此這個問題就可以以O(n^2)的複雜度解決了。
源代碼
/*
Author:楊林峯
Date:2017.11.28
NYOJ(104):最大和
*/
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int n, r, c, maxnum, sum, map[105][105];
memset(map, 0, sizeof(map));
cin >> n;
while (n--)
{
cin >> r >> c;
for (int i = 1; i <= r; i++)
{
for (int j = 0; j < c; j++)
{
cin >> map[i][j];
map[i][j] += map[i - 1][j]; //第i行存入的數據是前i行對應數據的和
}
}
int k;
/*求解每個組合的一維問題解,其最大值即爲該矩陣的解*/
for (k = 1, maxnum = map[1][0]; k <= r; k++)
for (int i = k; i <= r; i++)
{
/*求解一維問題*/
sum = 0;
for (int j = 0; j < c; j++)
{
sum += map[i][j] - map[k - 1][j];
if (sum < 0)
{
sum = 0;
}
else if (sum > maxnum)
{
maxnum = sum;
}
}
}
cout << maxnum << endl;
}
return 0;
}