Bzoj P1084 [SCOI2005]最大子矩阵___动态规划

题目大意:

有一个 n ∗ m n*m nm的矩阵,请你选出其中 K K K个子矩阵,使得这个 K K K个子矩阵分值之和最大,选出的 K K K个子矩阵不能相互重叠。
K K K个子矩阵分值之和最大为多少
1 ≤ n ≤ 100 , 1 ≤ m ≤ 2 , 1 ≤ K ≤ 10 1≤n≤100,1≤m≤2,1≤K≤10 1n100,1m2,1K10

分析:

注意到 1 ≤ m ≤ 2 1≤m≤2 1m2
分类讨论一下,
m = 1 m=1 m=1,是一个 n ∗ 1 n*1 n1的矩阵,把它看成一个长度为 n n n的序列
f i , j f_{i,j} fi,j表示前 i i i个数选了 j j j个区间的最大分值之和
S u m i , j Sum_{i,j} Sumi,j表示区间 [ i , j ] [i,j] [i,j]的分值之和,前缀和搞一下
转移显然:
初值 f i , j = f i − 1 , j f_{i,j} =f_{i-1,j} fi,j=fi1,j
转移时枚举一下最后一个区间是怎样的:
f i , j = m a x ( f i , j , f k , j − 1 + S u m k + 1 , i ) f_{i,j} = max(f_{i,j}, f_{k,j-1}+Sum_{k+1,i}) fi,j=max(fi,j,fk,j1+Sumk+1,i)
A n s w e r = f n , K Answer=f_{n,K} Answer=fn,K
m = 2 m=2 m=2,是一个 n ∗ 2 n*2 n2的矩阵,把它看成一个长度为 n n n,有2层的序列
f i , j , k f_{i,j,k} fi,j,k表示第一层前 i i i个第二层前 j j j个选了 k k k个子矩阵的最大分值之和
S u m k , i , j Sum_{k,i,j} Sumk,i,j k k k层的区间 [ i , j ] [i,j] [i,j]的分值之和
转移显然:
初值 f i , j , k = m a x ( f i − 1 , j , k , f i , j − 1 , k ) f_{i,j,k}=max(f_{i-1,j,k},f_{i,j-1,k}) fi,j,k=max(fi1,j,k,fi,j1,k)
转移时枚举一下最后一个矩阵是怎样的,
i ! = j i!=j i!=j时,最后一个选的子矩阵应该是 x ∗ 1 x*1 x1的,

f i , j , k = m a x ( f i , j , k , f l − 1 , j , k − 1 + S u m 1 , l , i ) f_{i,j,k}=max(f_{i,j,k},f_{l-1,j,k-1}+Sum_{1,l,i}) fi,j,k=max(fi,j,k,fl1,j,k1+Sum1,l,i)
f i , j , k = m a x ( f i , j , k , f i , l − 1 , k − 1 + S u m 2 , l , j ) f_{i,j,k}=max(f_{i,j,k},f_{i,l-1,k-1}+Sum_{2,l,j}) fi,j,k=max(fi,j,k,fi,l1,k1+Sum2,l,j)
特殊的,当 i = j i=j i=j时,除了上述 2 2 2个情况以外,最后一个选的矩阵也可以是 x ∗ 2 x*2 x2的,

f i , j , k = m a x ( f i , j , k , f l − 1 , l − 1 , k − 1 + S u m 1 , l , i + S u m 2 , l , j ) f_{i,j,k}=max(f_{i,j,k},f_{l-1,l-1,k-1}+Sum{1,l,i}+Sum_{2,l,j}) fi,j,k=max(fi,j,k,fl1,l1,k1+Sum1,l,i+Sum2,l,j)
A n s w e r = f n , n , K Answer=f_{n,n,K} Answer=fn,n,K

然后注意一下初值要赋一下一个较大的负数,否则可能会WA

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstdlib>
#include <algorithm>
 
#define inf 0x3f3f3f3f
#define N 105
#define M 15
 
using namespace std;
 
int Bdp[N][N][M], Adp[N][M], Sum[3][N], n, m, K;
 
void WorkA()
{
    for (int i = 0; i <= n; i++) 
        for (int j = 1; j <= K; j++) Adp[i][j] = -inf;
         
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= K; j++) 
        {
            Adp[i][j] = Adp[i - 1][j];
            for (int k = 1; k <= i; k++)
                Adp[i][j] = max(Adp[i][j], Adp[k - 1][j - 1] + (Sum[1][i] - Sum[1][k - 1]));
        }
    printf("%d\n", Adp[n][K]);
}
 
void WorkB()
{
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++) 
            for (int k = 1; k <= K; k++) Bdp[i][j][k] = -inf;
             
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            for (int k = 1; k <= K; k++)
            {  
                Bdp[i][j][k] = max(Bdp[i - 1][j][k], Bdp[i][j - 1][k]);        
                for (int l = 1; l <= i; l++)
                    Bdp[i][j][k] = max(Bdp[i][j][k], Bdp[l - 1][j][k - 1] + (Sum[1][i] - Sum[1][l - 1]));
                for (int l = 1; l <= j; l++)
                    Bdp[i][j][k] = max(Bdp[i][j][k], Bdp[i][l - 1][k - 1] + (Sum[2][j] - Sum[2][l - 1]));
                if (i == j)
                {
                    for (int l = 1; l <= i; l++)
                        Bdp[i][j][k] = max(Bdp[i][j][k], Bdp[l - 1][l - 1][k - 1] + (Sum[1][i] - Sum[1][l - 1]) + (Sum[2][i] - Sum[2][l - 1]));    
                }       
            }   
    printf("%d\n", Bdp[n][n][K]);
}
 
int main()
{
    scanf("%d %d %d", &n, &m, &K);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) 
        {
            int x; scanf("%d", &x);
            Sum[j][i] = Sum[j][i - 1] + x;
        }
    if (m == 1) WorkA(); else WorkB();
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值