Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 3540 Solved: 1771
[Submit][Status][Discuss]
Description
這裏有一個n*m的矩陣,請你選出其中k個子矩陣,使得這個k個子矩陣分值之和最大。注意:選出的k個子矩陣
不能相互重疊。
Input
第一行爲n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下來n行描述矩陣每行中的每個元素的分值(每個元素的
分值的絕對值不超過32767)。
Output
只有一行爲k個子矩陣分值之和最大爲多少。
Sample Input
3 2 2
1 -3
2 3
-2 3
Sample Output
9
m非常非常小,一開始還以爲和n一樣,苦惱了半天。。
感覺是狀壓,可是有這種奇怪的情況
11
10
11
10
11
按行掃描的狀壓會認爲它是五個矩陣,其實四個就行,就是說我們不知道這個新點是作爲原矩陣的一部分還是另成矩陣。。
SYQ大兄弟寫了巨型代碼,分類討論,很強
數據分治一下
m=1時,就是最大子段和問題,f[i][j][1/0]表示前i個,分了j段,且第i個選/不選的情況。
m=2時, 用掃描的思想,f[i][j][k]表示第一列掃到第i個,第二列掃到第j個,分了k個矩陣的情況。
轉移的時候討論列增加,或整塊增加。
用前綴和快速算增加量。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
inline int rd(){
int ret=0,f=1;char c;
while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
while(isdigit(c))ret=ret*10+c-'0',c=getchar();
return ret*f;
}
int n,m,lim;
int a[105][3];
int g[105][15][2];
void solve1(){
memset(g,0xcf,sizeof(g));
g[0][0][0]=g[0][0][1]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=lim;j++){
if(j)g[i][j][1]=max(g[i][lim][1],max(max(g[i-1][j-1][1],g[i-1][j-1][0]),g[i-1][j][1]))+a[i][1];
g[i][j][0]=max(g[i-1][j][0],g[i-1][j][1]);
}
}
cout<<max(g[n][lim][0],g[n][lim][1]);
}
int f[105][105][15];
int sum[105][105];
int both[105];
void solve2(){
// memset(f,0xcf,sizeof(f));
// f[0][0][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
sum[i][j]=sum[i-1][j]+a[i][j];
for(int i=1;i<=n;i++)
both[i]=both[i-1]+a[i][1]+a[i][2];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=lim;k>=1;k--){
int &now = f[i][j][k];
now=max(f[i-1][j][k],f[i][j-1][k]);
for(int l=1;l<=i;l++) now=max(now,f[l-1][j][k-1]+sum[i][1]-sum[l-1][1]);
for(int l=1;l<=j;l++) now=max(now,f[i][l-1][k-1]+sum[j][2]-sum[l-1][2]);
for(int l=1;l<=min(i,j);l++) now=max(now,f[l-1][l-1][k-1]+both[min(i,j)]-both[l-1]);
}
}
}
cout<<f[n][n][lim]<<endl;
}
int main(){
n=rd();m=rd();lim=rd();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j]=rd();
}
}
if(m==1) solve1();
else solve2();
return 0;
}