1. 程式人生 > >[SCOI2005]最大子矩陣

[SCOI2005]最大子矩陣

一行 turn iostream stream 最大子矩陣 情況 span 前綴和 ()

題目描述

這裏有一個n*m的矩陣,請你選出其中k個子矩陣,使得這個k個子矩陣分值之和最大。註意:選出的k個子矩陣不能相互重疊。

輸入輸出格式

輸入格式:

第一行為n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下來n行描述矩陣每行中的每個元素的分值(每個元素的分值的絕對值不超過32767)。

輸出格式:

只有一行為k個子矩陣分值之和最大為多少。

輸入輸出樣例

輸入樣例#1:
3 2 2
1 -3
2 3
-2 3
輸出樣例#1:
9

解析:

剛開始看並沒有思路,直到看到了數據範圍 m <= 2好吧

最多也就是兩行的矩陣

這樣的話我們可以暴力手模一下情況,就可以看出轉移有那種情況

dp[i][j][k] 表示第一行選取到了第i個,第二行選取了第j個,選取了k個矩形的最大取值

之後分析一下

dp[i ][j][k]可以由哪些情況轉移來呢

1.啥都不拿-.- 繼承父輩的衣缽 在 dp[i - 1 ][j][k] 和dp[i][j - 1][k] 中去較大值

2.拿第一列的,枚舉一下從哪裏開始拿,

3.拿第二列的,枚舉一下從哪裏開始拿

4.拿兩列的,枚舉一下從哪裏開始拿

然後涉及大量求和

前綴和優化

代碼:

#include<cstdio>
#include
<iostream> #include<algorithm> using namespace std; int note[101][3]; int sum1[101]; int sum2[101]; int sum12[101]; int dp[101][101][15]; int main() { int n,m,k; scanf("%d%d%d",&n,&m,&k); for(int i = 1;i <= n;i++) { for(int j = 1;j <= m;j++) { scanf(
"%d",&note[i][j]); } sum1[i] = sum1[i - 1] + note[i][1]; sum2[i] = sum2[i - 1] + note[i][2]; sum12[i] = sum12[i - 1] + note[i][1] + note[i][2]; } for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) { for(int z = k;z >= 1;z--) { dp[i][j][z] = max(dp[i - 1][j][z],dp[i][j - 1][z]);// 第一種 for(int op = 1;op <= i;op++) dp[i][j][z] = max(dp[i][j][z],dp[op - 1][j][z - 1] + sum1[i] - sum1[op - 1]);//第二種 for(int op = 1;op <= j;op++) dp[i][j][z] = max(dp[i][j][z],dp[i][op - 1][z - 1] + sum2[j] - sum2[op - 1]);//第三種 for(int op = 1;op <= min(i,j);op++) dp[i][j][z] = max(dp[i][j][z],dp[op - 1][op - 1][z - 1] + sum12[min(i,j)] - sum12[op - 1]);//第四種 } } printf("%d",dp[n][n][k]); return 0; }

[SCOI2005]最大子矩陣