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

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[k1][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[k1][p][j]+sum[i][1]sum[p][1]);

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

还有一种情况,就是当我们选的矩阵的宽等于2时,也就是i==j时
转移方程:
f[k][i][j]=max(f[k][i][j],f[k1][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][n];
    }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][n][n];
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2018-01-28 16:54  XSamsara  阅读(44)  评论(0编辑  收藏