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
解题报告:这数据范围就看出是个SBT,但本人太菜,写的又丑又长,复杂度又高.
定义f[i][j][k]表示第一列的前i列第二行的前j列都处理完,一共选出了k个的最大分值之和,这个显然是很好转移的:枚举矩阵大小和形状,然后分情况转移即可
复杂度(O(n^3k))
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=105,inf=2e8;
int f[N][N][15],a[N][3],g[3][N][N],xy[N][N];
void work()
{
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",&a[i][j]);
for(int j=1;j<=2;j++){
//g[j][i][k]表示第j列的i-k一段的最大子段和
for(int i=1;i<=n;i++){
for(int k=1;k<=i;k++){
int tot=0,ret=-inf;
for(int l=i;l>=k;l--){
if(tot<0)tot=a[l][j];
else tot+=a[l][j];
if(tot>ret)ret=tot;
}
g[j][k][i]=ret;
}
}
}
for(int i=1;i<=n;i++){
//xy[i][j]表示前两列一起时 i-j的最大子段和
for(int k=1;k<=i;k++){
int tot=0,ret=-inf;
for(int l=i;l>=k;l--){
if(tot<0)tot=a[l][1]+a[l][2];
else tot+=a[l][1]+a[l][2];
if(tot>ret)ret=tot;
}
xy[k][i]=ret;
}
}
int s,ans=-inf;
for(int k=0;k<K;k++)
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
f[i][j][k+1]=Max(f[i][j][k+1],f[i][j][k]);
s=Max(i,j)+1;
for(int l=s;l<=n;l++){
f[l][l][k+1]=Max(f[l][l][k+1],f[i][j][k]+xy[s][l]);
}
for(int l=i+1;l<=n;l++)
f[l][j][k+1]=Max(f[l][j][k+1],f[i][j][k]+g[1][i+1][l]);
for(int l=j+1;l<=n;l++)
f[i][l][k+1]=Max(f[i][l][k+1],f[i][j][k]+g[2][j+1][l]);
}
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
ans=Max(ans,f[i][j][K]);
printf("%d
",ans);
}
int main()
{
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
work();
return 0;
}