【最大立方体和】吃西瓜rqnoj93

吃西瓜rqnoj93

题目描述

[说明]此题中出现的所有数全为整数
[背景]SubRaY有一天得到一块西瓜,是长方体形的....
[题目描述]SubRaY发现这块西瓜长m厘米,宽n厘米,高h厘米.他发现如果把这块西瓜平均地分成m*n*h块1立方厘米的小正方体,那么每一小块都会有一个营养值(可能为负,因为西瓜是有可能坏掉的,但是绝对值不超过200).
现在SubRaY决定从这m*n*h立方厘米的西瓜中切出mm*nn*hh立方厘米的一块小西瓜(一定是立方体形,长宽高均为整数),然后吃掉它.他想知道他最多能获得多少营养值.(0<=mm<=m,0<=nn<=n,0<=hh<=h.mm,nn,hh的值由您来决定).
换句话说,我们希望从一个m*n*h的三维矩阵中,找出一个三维子矩阵,这个子矩阵的权和最大.


一个2*3*4的例子,最优方案为切红色2*3*1部分
[数据范围]
对于30%的数据,h=1,1<=m,n<=10
对于全部的数据,1<=h<=32,1<=m,n<=50,保证h<=m,n

输入格式

首行三个数h,m,n(注意顺序),分别表示西瓜的高,长,宽.
以下h部分,每部分是一个m*n的矩阵,第i部分第j行的第k个数表示西瓜第i层,第j行第k列的那块1立方厘米的小正方体的营养值.

输出格式

SubRaY所能得到的最大营养值

样例输入

2 3 4
4 1 2 8
0 5 -48 4
3 0 1 9
2 1 4 9
1 0 1 7
3 1 2 8

样例输出

45

 

 

前面发过两道题,可以搜索,旅游路线和最大加权矩形

旅游路线 相当于要求取一条线来取最大子区间和,是一维的 ,时间效率O(n)

最大加权矩形 是取一个矩阵,需要压缩成一维的,就和上面一样了,总体是二维的,时间效率O(n3)

而这一题,求一个最大立方体,就需要压缩成矩阵,再压缩成线,总体是三维的,时间效率O(n5)

 

 

首先说说怎么压缩,最大加权矩形我们用sum[i][j]表示前 i 行的第 j 列的和,这里我们可以一次类推,用sum[k][i][j]表示前 k 层、前 i 行的第 j 列的和,这里在放一下图距离说明一下

例如sum[2][2][2]就表示前2层前2行的第二列的和(图中绿色的部分)

在读入数据的时候就可以维护出来每一层的sum[k][i][j]=sum[k][i-1][j]+a[i][j];
读完后再来一次循环加上前面几层的 sum[k][i][j]+=sum[k-1][i][j];

然后(和前面说的几题一样,只是多了一个高度)我们就枚举层(上界k 和 下界kk),枚举行(上界i 和下界ii),再枚举列j
现在就需要把他们压缩了,比如当循环到k=2,kk=2,i=3,ii=3,j=4就为上图中的蓝色部分的一个,把他们压缩出来记为cc,用下图来说明

那么cc=sum[kk][ii][j]-sum[kk][i-1][j]-(sum[k-1][ii][j]-sum[k-1][i-1][j]);      (颜色与图中对应,应该很好看懂了)

剩下的就是经典问题了,f[j]=max(f[j]+cc,cc);

C++ Code

/*
C++ Code
http://blog.csdn.net/jiangzh7
*/
#include<cstdio>
#include<cstring>
using namespace std;
#define MAXH 40
#define MAXN 60
#define max(a,b) (a)>(b)?(a):(b)
#define INF 0x7fffffff

int h,n,m;
int a[MAXH][MAXN][MAXN];
int sum[MAXH][MAXN][MAXN];//sum[k][i][j]前 k 层的前 i 行的第 j 列的和
int f[MAXN];

void debug()
{
    int k,i,j;
    for(k=1;k<=h;k++)
    {
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
                printf("%d ",sum[k][i][j]);
            printf("\n");
        }
        printf("\n");
    }
    printf("\n");
}

int main()
{
    freopen("rqn93.in","r",stdin);
    freopen("rqn93.out","w",stdout);
    scanf("%d%d%d",&h,&n,&m);
    int k,i,j;
    for(k=1;k<=h;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
            {
                scanf("%d",&a[k][i][j]);
                sum[k][i][j]=sum[k][i-1][j]+a[k][i][j];
            }
    //debug();
    for(k=1;k<=h;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                sum[k][i][j]+=sum[k-1][i][j];
    //debug();
    int kk,ii,cc;
    int maxx=-INF;
    for(k=1;k<=h;k++)
        for(kk=k;kk<=h;kk++)
            for(i=1;i<=n;i++)
                for(ii=i;ii<=n;ii++)
                {
                    f[0]=0;
                    for(j=1;j<=m;j++)
                    {
                        //printf("k=%d;kk=%d;i=%d;ii=%d;j=%d;",k,kk,i,ii,j);
                        cc=sum[kk][ii][j]-sum[kk][i-1][j]-(sum[k-1][ii][j]-sum[k-1][i-1][j]);
                        f[j]=max(f[j-1]+cc,cc);
                        maxx>?=f[j];
                        //printf("maxx=%d\n",maxx);
                    }
                }
    printf("%d",maxx);
    return 0;
}

  

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值