最大加权子矩阵问题
題目描述
為了更好的備戰 NOIP2013,電腦組的幾個女孩子 LYQ,ZSC,ZHQ 認為,我們不光需要機房,我們還需要運動,于是就決定找校長申請一塊電腦組的課余運動場地,聽說她們都是電腦組的高手,校長沒有馬上答應他們,而是先給她們出了一道數學題,并且告訴她們:你們能獲得的運動場地的面積就是你們能找到的這個最大的數字。
校長先給他們一個N * N 矩陣。要求矩陣中最大加權矩形,即矩陣的每一個元素都有一權值,權值定義在整數集上。從中找一矩形,矩形大小無限制,是其中包含的所有元素的和最大 。矩陣的每個元素屬于 [-127,127][?127,127] ,例如
0 –2 –7 0 9 2 –6 2 -4 1 –4 1 -1 8 0 –2在左下角:
9 2 -4 1 -1 8和為 15。
幾個女孩子有點犯難了,于是就找到了電腦組精打細算的 HZH,TZY 小朋友幫忙計算,但是遺憾的是他們的答案都不一樣,涉及土地的事情我們可不能含糊,你能幫忙計算出校長所給的矩形中加權和最大的矩形嗎?
輸入輸出樣例
輸入:
4 0 -2 -7 09 2 -6 2 -4 1 -4 1 -1 8 0 -2輸出:
15引子:
在最大子段和中:
DP方程:p[i]=max(dp[i-1]+tmp,tmp),tmp表示這個數列的第i項。
核心代碼:
那么我們如何來處理這一題呢?
我們可以考慮將矩形壓縮成一維,比如處理一個2行的矩形時,將a [ i ][ j ]與a [ i - 1 ][ j ]相加,成為一個新的數組f [ n ],再使用上述代碼進行動態規劃,找出局部最優解。
那如何來快速將矩形折疊呢?
我們可以選擇前綴和
簡單來說,就是在輸入的時候,再次加上a[ i - 1 ][ j ],這樣可以用減法來快速表示壓縮的矩形。
具體代碼如下:
scanf("%d",&n);int i,j;for(i=1;i<=n;++i){for(j=1;j<=n;++j){scanf("%d",&a[i][j]);a[i][j]+=a[i-1][j];//根據前綴和定義處理}}用樣例來表示,輸入的是:
0 -2 -7 09 2 -6 2-4 1 -4 1 -1 8 0 -2在經過前綴和處理之后,輸出的是這個:
0 -2 -7 09 0 -13 25 1 -17 34 9 -17 1可以模擬一下,a[ i ][ j ] - a[ i - k ][ j ]正好是以i為最下面一行,往上k行的壓縮結果,這就很方便地表示了壓縮后的矩形。
那又怎么循環找出各行為最下一行,不同行數的矩陣最大值呢?
用i表示以i為最下一行,k表示向上k行,代碼如下:
for(i=1;i<=n;++i){for(k=1;k<=i;++k){}}k<=i,保證了i-k>=0。
那再次循環,運用第一個代碼的簡單變形,可以求出以i為最下一行,向上k行的矩形最大值,多次更新ans,愉快AC。
總代碼為:
另外再提供一種更便于理解的較為暴力的算法:
const int N = 130; int n, s[N][N]; int ans; int main() {scanf("%d", &n);for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)scanf("%d", &s[i][j]);for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];for (int x1 = 1; x1 < n; x1++)for (int y1 = 1; y1 < n; y1++)for (int x2 = x1; x2 <= n; x2++)for (int y2 = y1; y2 <= n; y2++)ans = max(ans, s[x2][y2] + s[x1 - 1][y1 - 1] - s[x2][y1 - 1] - s[x1 - 1][y2]);cout << ans;return 0; }總結
- 上一篇: SpringBoot整合Mybatis(
- 下一篇: 优酷土豆路由宝刷固件改无线打印服务器笔记