[SCOI2005]最大子矩阵 (动态规划)

题目描述

这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。注意:选出的k个子矩阵不能相互重叠。

输入输出格式

输入格式:

第一行为n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下来n行描述矩阵每行中的每个元素的分值(每个元素的分值的绝对值不超过32767)。 

输出格式: 

只有一行为k个子矩阵分值之和最大为多少。

 

输入输出样例

输入样例#1: 
3 2 2
1 -3
2 3
-2 3
输出样例#1: 
  9
 

Solution

这道题作为一道省选DP来讲的,偏简单了一些.
但是还是有一点思维难度的.
拿到先看 m , m 只有 1 和 2 ?
所以先打了一下 m=1 的情况.
 

状态定义:

f[i][l] 表示到第 i 个点 用掉 l 个矩形的最大值.

转移方程:

for(pre 1--> i-1)

f[i][l]=max(f[i-1][l],f[pre][l-1]+sum[pre-->i]); //sum 表示pre到i的元素值的和.

 

于是 m=1 便有30 pts.

 

然后再想 m=2 , 由 m=1 拓展?

于是 定义状态 : f[ i ][ j ][ l ] 表示上面一列到了 i 下面一列到了 j 已选择 l 个矩阵的最大值.

想了想,m=2有一下几种情况:

1. 这个点我不做拓展  --> max( f[ i-1 ][ j-1 ][ l ] , f[ i-1 ][ j-1 ][ l ] ,f[ i ][ j-1 ][ l ] ) ; 

2. 由上一列扩展一个小的 s*1 面积的

3. 由上一列扩展一个小的 s*1 面积的

4. 两列都作扩展 ,来一个 s*2 面积的

 

于是乎,这道题的 DP 也自然就出来了.

 

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int f1[105][11],f[105][105][11];
int c[2][105],sum[2][105];

void solve()    
{
    for(int i=1;i<=n;i++)
    for(int l=1;l<=k;l++) 
        { 
            f1[i][l]=f1[i-1][l];
            for(int j=0;j<i;j++)
            f1[i][l]=max(f1[j][l-1]+sum[1][i]-sum[1][j],f1[i][l]);
        }
    cout<<f1[n][k];
    return;
}


int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",&c[j][i]),sum[j][i]=sum[j][i-1]+c[j][i];
    if(m==1) {solve();return 0;}
    
    for(int l=1;l<=k;l++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++) 
    {
            f[i][j][l]=max(f[i-1][j][l],f[i][j-1][l]);
            for(int pre=0;pre<i;pre++) f[i][j][l]=max(f[i][j][l],f[pre][j][l-1]+sum[1][i]-sum[1][pre]);
            for(int pre=0;pre<j;pre++) f[i][j][l]=max(f[i][j][l],f[i][pre][l-1]+sum[2][j]-sum[2][pre]);
            if(i==j)
            for(int pre=0;pre<i;pre++)
            f[i][j][l]=max(f[i][j][l],f[pre][pre][l-1]+sum[1][i]-sum[1][pre]+sum[2][j]-sum[2][pre]);
    }
    cout<<f[n][n][k];

    return 0;
}

 

 

 

posted @ 2018-05-08 22:10  Kevin_naticl  阅读(193)  评论(0编辑  收藏  举报