[ SCOI 2005 ] 最大子矩阵

\(\\\)

\(Description\)


给出一个\(N\times M\)的有权矩阵,选出其中\(K\)个互不重叠的子矩阵,使得这\(K\)个子矩阵的权值和最大。

  • \(N\in [1,100]\)\(M\in \{1,2\}\)\(K\in [1,10]\)

\(\\\)

\(Solution\)


  • 对于\(M=1\)的情况,矩阵是一个长为\(N\)的数列,问题变为\(K\)段最大子段和,可以参考:最大子段和系列问题

  • 对于\(M=2\)的情况,沿用上一情况的做法,设状态\(f[i][j][S]\)表示当前处理到第\(i\)行,已经选了\(j\)个子矩阵,本行选则方式为\(S\)的方案数。

  • 分析情况可知,\(S\)有一下五种:

    • \(0\):两个位置都不选
    • \(1\):只选左边的元素
    • \(2\):只选右边的元素
    • \(3\):两个都选,放在一个矩形里
    • \(4\):两个都选,放在两个矩形里
  • 对五种状态分别讨论转移即可:

    • \(0\)状态无需考虑任何限制,直接从上一行的五种状态转移:\(\begin{align}f[i][j][0]=max\{f[i-1][j][0/1/2/3/4]\}\end{align}\)
    • \(1\)状态只能从\(1\)状态和\(4\)状态直接继承,其他都得从上一个矩形转移:\(\begin{align}f[i][j][1]=a[i][0]+max\{f[i-1][j-1][0/2/3],f[i-1][j][1/4]\}\end{align}\)
    • \(2\)状态只能从\(2\)状态和\(4\)状态直接转移,其他都得从上一个矩形转移:\(\begin{align}f[i][j][2]=a[i][1]+max\{f[i-1][j-1][0/1/3],f[i-1][j][2/4]\}\end{align}\)
    • \(3\)状态只能从\(3\)状态直接转移,定义\(s[i]=a[i][0]+a[i][1]\)\(\begin{align}f[i][j][3]=s[i]+max\{f[i-1][j-1][0/1/2/4],f[i-1][j][3]\}\end{align}\)
    • \(4\)状态只能直接从\(4\)状态转移,但是可以利用\(1\)状态和\(2\)状态的矩阵:\(\begin{align}f[i][j][4]=s[i]+max\{f[i-1][j-2][0/3],f[i-1][j-1][1/2],f[i-1][j][4]\}\end{align}\)
  • 需要注意的是,初始化只有所有的\(0\)状态为\(0\),其他应该是\(-\infty\),防止一些转移的初始状态不存在。

\(\\\)

\(Code\)


#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 110
#define K 15
#define R register
#define gc getchar
using namespace std;

inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}

int n,m,k,a[N][2],f[N][K][5];

inline void work1(){
  for(R int i=1;i<=n;++i) a[i][0]=rd();
  for(R int i=1;i<=n;++i)
    for(R int j=1;j<=min(i,k);++j){
      f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
      f[i][j][1]=a[i][0]+max(f[i-1][j][1],max(f[i-1][j-1][0],f[i-1][j-1][1]));
    }
  printf("%d\n",max(f[n][k][1],f[n][k][0]));
};

inline int mx(int a,int b,int c,int d,int e){
  return max(max(a,b),max(max(c,d),e));
}

inline void work2(){
  memset(f,0xcf,sizeof(f));
  for(R int i=0;i<=n;++i)
    for(R int j=0;j<=k;++j) f[i][j][0]=0;
  for(R int i=1;i<=n;++i){a[i][0]=rd();a[i][1]=rd();}
  for(R int i=1;i<=n;++i)
    for(R int j=1;j<=k;++j){
      f[i][j][0]=mx(f[i-1][j][0],f[i-1][j][1],f[i-1][j][2],f[i-1][j][3],f[i-1][j][4]);
      f[i][j][1]=a[i][0]+mx(f[i-1][j][1],f[i-1][j][4],f[i-1][j-1][0],f[i-1][j-1][2],f[i-1][j-1][3]);
      f[i][j][2]=a[i][1]+mx(f[i-1][j][2],f[i-1][j][4],f[i-1][j-1][0],f[i-1][j-1][1],f[i-1][j-1][3]);
      f[i][j][3]=a[i][0]+a[i][1]+mx(f[i-1][j][3],f[i-1][j-1][0],f[i-1][j-1][1],f[i-1][j-1][2],f[i-1][j-1][4]);
      f[i][j][4]=a[i][0]+a[i][1]+max(f[i-1][j][4],max(f[i-1][j-1][1],f[i-1][j-1][2]));
      if(j>=2) f[i][j][4]=max(f[i][j][4],a[i][0]+a[i][1]+max(f[i-1][j-2][0],f[i-1][j-2][3]));
    }
  printf("%d\n",mx(f[n][k][0],f[n][k][1],f[n][k][2],f[n][k][3],f[n][k][4]));
};

int main(){
  n=rd(); m=rd(); k=rd();
  (m==1)?work1():work2();
  return 0;
}
posted @ 2018-09-01 16:46  SGCollin  阅读(157)  评论(0编辑  收藏  举报