[SCOI2005]最大子矩阵

题目

BZOJ 1084: [SCOI2005]最大子矩阵

分析

一眼看上去就是一道DP!想了很久,码出来一个很复杂的做法,而且还错了……
注意到m<=2,可以利用这个性质:
当m=1时,问题变为求多个子区间使得和最大。这就比较好想了,令f[t][i]表示选到了第i个位置,已经选了t个区间的最大值。得到: f[t][i]=max{f[t][i],f[t1][j]+sum[j,i]},j[1,i]; f [ t ] [ i ] = m a x { f [ t ] [ i ] , f [ t − 1 ] [ j ] + s u m [ j , i ] } , j ∈ [ 1 , i ] ;
特别的,如果前t-1个矩阵都只有一个元素,那么可以不必枚举额外的j,直接将前面的数加过来: f[t][t]=sum[0,t]; f [ t ] [ t ] = s u m [ 0 , t ] ;
当m=2时,我们再分类讨论:
对于第i行的某一列j,它的答案可能来自于:

  • 该列前若干行s的答案加上[s,i]这几行第j列的值;
  • 另一列j’前若干行s的答案加上[s,i]这几行第j’列的值;
  • 如果可以两列同步转移,答案就是这几行两列的值。

设f[t][i][j]表示选了t个区间,其中第0列选到了i,第1列选到了j时的最大值。稍加思考,可得到:

f[t][i][j]=max{f[t][i][j],f[t1][s][j]+sum[0][s,i],f[t1][i][s]+sum[1][s,j]}; f [ t ] [ i ] [ j ] = m a x { f [ t ] [ i ] [ j ] , f [ t − 1 ] [ s ] [ j ] + s u m [ 0 ] [ s , i ] , f [ t − 1 ] [ i ] [ s ′ ] + s u m [ 1 ] [ s ′ , j ] } ;
f[t][i][i]=max(f[t][i][i],f[t1][s][s]+sum[0][s,i]+sum[1][s,i],s[1,i],s[1,j]; f [ t ] [ i ] [ i ] = m a x ( f [ t ] [ i ] [ i ] , f [ t − 1 ] [ s ] [ s ] + s u m [ 0 ] [ s , i ] + s u m [ 1 ] [ s , i ] , s ∈ [ 1 , i ] , s ′ ∈ [ 1 , j ] ;

多看几遍转移方程自然理解此题,自然地用上前缀和优化。时间复杂度 O(n3k) O ( n 3 k ) 。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=102;
int a[maxn][3],sum[maxn][3];
int f[12][maxn][maxn];
int n,m,k,ans;

int main()
{   
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        for(int j=0;j<m;j++)
            scanf("%d",&a[i][j]),sum[i][j]=sum[i-1][j]+a[i][j];
    if(m==1)
    {
        for(int i=1;i<=n;i++)
            for(int t=1;t<=k;t++)
            {
                if(i==t)
                {
                    f[t][i][0]=sum[i][0];
                    continue;
                }
                f[t][i][0]=f[t][i-1][0];
                for(int j=t-1;j<i;j++)
                    f[t][i][0]=max(f[t][i][0],f[t-1][j][0]+sum[i][0]-sum[j][0]);            
            }
        printf("%d",f[k][n][0]);
        return 0;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int t=1;t<=k;t++)
            {
                f[t][i][j]=max(f[t][i-1][j],f[t][i][j-1]);
                for(int l=0;l<i;l++)    f[t][i][j]=max(f[t][i][j],f[t-1][l][j]+sum[i][0]-sum[l][0]);
                for(int l=0;l<j;l++)    f[t][i][j]=max(f[t][i][j],f[t-1][i][l]+sum[j][1]-sum[l][1]);
                if(i==j)
                    for(int l=0;l<i;l++)
                        f[t][i][j]=max(f[t][i][j],f[t-1][l][l]+sum[i][0]-sum[l][0]+sum[j][1]-sum[l][1]);
            }
    printf("%d",f[k][n][n]);
    return 0;
}
/*附赠便于观察的样例格式
3 2 2
 1 -3
 2  3
-2  3
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值