您的位置:首页 > 其它

BZOJ 1084: [SCOI2005]最大子矩阵【DP】

2018-01-28 16:53 218 查看

1084: [SCOI2005]最大子矩阵

Time Limit: 10 Sec Memory Limit: 162 MB

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

题解

这题很明显就是DP,虽然DFS也能做,但是这里主要讲DP的解法。

我们读题后会发现m只有1和2的情况,那么,分类讨论。

1.m=1

矩阵就退化成了一条线。

我们设f[k][i]表示从1~i中挑选k个连续字段的最优解,那么很容易想到在前面枚举一个j,从j+1~i为一个新的字段。

转移方程:f[k][i]=max(f[k][i],f[k−1][j]+sum[i]−sum[j])

sum表示前缀和。

2.m=2

我们有了m=1的想法,那么m=2就好想了。

那么就再加上一维,f[k][i][j]表示第一列挑选到i,第二列挑选到j,共有k个矩阵的最优解。

在前面枚举个p。

转移方程:

f[k][i][j]=max(f[k][i][j],f[k−1][p][j]+sum[i][1]−sum[p][1]);

f[k][i][j]=max(f[k][i][j],f[k−1][i][p]+sum[j][2]−sum[p][2]);

还有一种情况,就是当我们选的矩阵的宽等于2时,也就是i==j时

转移方程:

f[k][i][j]=max(f[k][i][j],f[k−1][p][p]+mp[i][1]−mp[p][1]+mp[j][2]−mp[p][2]);

下面贴上代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,K,ans,mp[105][5],d[15][105],f[15][105][105];
int main(){
scanf("%d%d%d",&n,&m,&K);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]),mp[i][j]+=mp[i-1][j];
if(m==1){
for(int k=1;k<=K;k++)
for(int i=1;i<=n;i++){
d[k][i]=d[k][i-1];
for(int j=0;j<i;j++) d[k][i]=max(d[k][i],d[k-1][j]+mp[i][1]-mp[j][1]);
}
ans=d[K]
;
}else{
for(int k=1;k<=K;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
f[k][i][j]=max(f[k][i-1][j],f[k][i][j-1]);
for(int p=0;p<i;p++) f[k][i][j]=max(f[k][i][j],f[k-1][p][j]+mp[i][1]-mp[p][1]);
for(int p=0;p<j;p++) f[k][i][j]=max(f[k][i][j],f[k-1][i][p]+mp[j][2]-mp[p][2]);
if(i==j)
for(int p=0;p<j;p++) f[k][i][j]=max(f[k][i][j],f[k-1][p][p]+mp[i][1]-mp[p][1]+mp[j][2]-mp[p][2]);
}
ans=f[K]

;
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: