BZOJ 1084 [SCOI 2005] DP 解题报告

1084: [SCOI2005]最大子矩阵

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

【解题报告】

题解来自http://blog.csdn.net/zz_ylolita/article/details/50647536
注意到m=1或者2,当m=1时,是普通的最大连续字段和,只不过是k个:
设f[i][j]表示前i个数中取出j个矩形的最大和
选:f[i][j]=max{f[i1][j-1]+s[i]-s[i1-1]}
不选:f[i][j]=max(f[i][j],f[i-1][j])
复杂度O(n^2*K)
当m=2时,设f[i][j][k]表示第一列选到第i个数,第二列选到第j个数时,总共k个子矩形的答案
转移有4种情况
当这一位什么都不做的时候:f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k]) //这里有点像最长公共子序列的转移,也是背包问题
当仅选取第一列的某段区间时:f[i][j][k]=max(f[i1][j][k-1]+sum[i][1]-sum[i1-1][1]) 1<=i1<=i-1
当仅选取第二列的某段区间时:f[i][j][k]=max(f[i][j1][k-1]+sum[j][2]-sum[j1-1][2]) 1<=j1<=j-1
当i==j时,可以选取两列一起的矩形:f[i][j][k]=max(f[i1][i1][k]+sum[i][1]+sum[i][2]-sum[i1-1][1]-sum[i1-1][2])
最后所有情况取max
复杂度O(n^3*K)

代码如下:

/**************************************************************
    Problem: 1084
    User: onepointo
    Language: C++
    Result: Accepted
    Time:80 ms
    Memory:1536 kb
****************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 110
#define inf 0x3f3f3f3f

int n,m,K;
int f[N][N][15],s[N][15];

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",&s[i][j]);
        s[i][j]+=s[i-1][j];
    }
    if(m==1)
    {
        for(int i=1;i<=n;++i)
        for(int k=1;k<=K;++k)
        {
            f[i][0][k]=f[i-1][0][k];
            for(int j=k-1;j<i;++j)
            {
                f[i][0][k]=max(f[j][0][k-1]+s[i][1]-s[j][1],f[i][0][k]);
            }
        }
        printf("%d\n",f[n][0][K]);
    }
    if(m==2)
    {
        for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        for(int k=1;k<=K;++k)
        {
            f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k]);
            for(int u=0;u<i;u++) f[i][j][k]=max(f[i][j][k],f[u][j][k-1]+s[i][1]-s[u][1]);
            for(int u=0;u<j;u++) f[i][j][k]=max(f[i][j][k],f[i][u][k-1]+s[j][2]-s[u][2]);
            if(i==j) for(int u=0;u<i;u++) f[i][j][k]=max(f[i][j][k],f[u][u][k-1]+s[i][1]-s[u][1]+s[i][2]-s[u][2]);
        }
        printf("%d\n",f[n][n][K]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值