bzoj1084: [SCOI2005]最大子矩阵

来源:互联网 发布:java和scala 编辑:程序博客网 时间:2024/05/22 03:33

题解

  虽然是12年前的题但是并不好做。
  注意到m=1m=2
  当m=1时,g[i][k]表示前i个数选了k段组成的和最大是多少,且我强制选择第i个数。
  当m=2时,f[i][j][k]表示考虑了第一行的前i个,第二行的前j个数,选出k个子矩阵的最大和,不强制选择末尾的数字。
  转移可以预处理最大连续子段和。

题外话

4 2 84 23 -2-1 -55 4

这组数据会把网上的大部分题解卡掉

代码

//动态规划#include <cstdio>#include <algorithm>#define maxn 110#define inf 0x3f3f3f3fusing namespace std;int f[maxn][maxn][15], g[maxn][15], a[maxn][maxn], b[maxn][maxn], c[maxn][maxn], N, K,    M, w[maxn][maxn];int main(){    int i, j, k, ans=-inf, t;    scanf("%d%d%d",&N,&M,&K);    for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",&w[j][i]);    for(i=1;i<=N;i++)for(j=i;j<=N;j++)a[i][j]=max(a[i][j-1],0)+w[1][j];    for(i=1;i<=N;i++)for(j=i;j<=N;j++)b[i][j]=max(b[i][j-1],0)+w[2][j];    for(i=1;i<=N;i++)for(j=i;j<=N;j++)c[i][j]=max(0,c[i][j-1])+w[1][j]+w[2][j];    if(M==1)    {        for(i=1;i<=K;i++)g[0][i]=-inf;        for(i=1;i<=N;i++)            for(k=1;k<=K;k++)            {                g[i][k]=-inf;                for(j=0;j<i;j++)g[i][k]=max(g[i][k],g[j][k-1]+a[j+1][i]);            }        for(i=1;i<=N;i++)ans=max(ans,g[i][K]);    }    if(M==2)    {        for(i=0;i<=N;i++)for(j=1;j<=K;j++)f[i][0][j]=f[0][i][j]=-inf;        for(i=0;i<=N;i++)            for(j=0;j<=N;j++)                for(k=1;k<=K;k++)                {                    if(i)f[i][j][k]=max(f[i][j][k],f[i-1][j][k]);                    if(j)f[i][j][k]=max(f[i][j][k],f[i][j-1][k]);                    if(i==j)                        for(t=0;t<i;t++)                            f[i][i][k]=max(f[i][i][k],f[t][t][k-1]+c[t+1][i]);                    for(t=0;t<i;t++)f[i][j][k]=max(f[i][j][k],f[t][j][k-1]+a[t+1][i]);                    for(t=0;t<j;t++)f[i][j][k]=max(f[i][j][k],f[i][t][k-1]+b[t+1][j]);                    for(t=0;t<min(i,j);t++)f[i][j][k]=max(f[i][j][k],f[t][t][k-1]);                    ans=max(ans,f[i][j][k]);                }        ans=f[N][N][K];    }    printf("%d\n",ans);    return 0;}
0 0