[BZOJ] 1084 [SCOI2005]最大子矩阵

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3540  Solved: 1771
[Submit][Status][Discuss]
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

m非常非常小,一开始还以为和n一样,苦恼了半天。。
感觉是状压,可是有这种奇怪的情况
11
10
11
10
11
按行扫描的状压会认为它是五个矩阵,其实四个就行,就是说我们不知道这个新点是作为原矩阵的一部分还是另成矩阵。。

SYQ大兄弟写了巨型代码,分类讨论,很强

数据分治一下

m=1时,就是最大子段和问题,f[i][j][1/0]表示前i个,分了j段,且第i个选/不选的情况。

m=2时, 用扫描的思想,f[i][j][k]表示第一列扫到第i个,第二列扫到第j个,分了k个矩阵的情况。
转移的时候讨论列增加,或整块增加。
用前缀和快速算增加量。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

inline int rd(){
    int ret=0,f=1;char c;
    while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret*f;
}

int n,m,lim;

int a[105][3];

int g[105][15][2];
void solve1(){
    memset(g,0xcf,sizeof(g));
    g[0][0][0]=g[0][0][1]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=lim;j++){
            if(j)g[i][j][1]=max(g[i][lim][1],max(max(g[i-1][j-1][1],g[i-1][j-1][0]),g[i-1][j][1]))+a[i][1];
            g[i][j][0]=max(g[i-1][j][0],g[i-1][j][1]);
        }
    }
    cout<<max(g[n][lim][0],g[n][lim][1]);
}


int f[105][105][15];
int sum[105][105];
int both[105];
void solve2(){
//  memset(f,0xcf,sizeof(f));
//  f[0][0][0]=0;
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=m;j++)
            sum[i][j]=sum[i-1][j]+a[i][j];
    for(int i=1;i<=n;i++)
        both[i]=both[i-1]+a[i][1]+a[i][2];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            for(int k=lim;k>=1;k--){
                int &now = f[i][j][k];
                now=max(f[i-1][j][k],f[i][j-1][k]);
                for(int l=1;l<=i;l++) now=max(now,f[l-1][j][k-1]+sum[i][1]-sum[l-1][1]);
                for(int l=1;l<=j;l++) now=max(now,f[i][l-1][k-1]+sum[j][2]-sum[l-1][2]);
                for(int l=1;l<=min(i,j);l++) now=max(now,f[l-1][l-1][k-1]+both[min(i,j)]-both[l-1]);
            }
        }
    }
    cout<<f[n][n][lim]<<endl;
}

int main(){
    n=rd();m=rd();lim=rd();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[i][j]=rd();
        }
    }
    if(m==1) solve1();
    else solve2();
    return 0;
}
posted @ 2018-06-06 09:32  GhostCai  阅读(103)  评论(0编辑  收藏  举报