【UVA10755】Garbage Heap

14 篇文章 0 订阅

题面

  Farmer John has a heap of garbage formed in a rectangular paral- lelepiped.
  It consists of A × B × C garbage pieces each of which has a value.
  The value of a piece may be 0, if the piece is neither profitable nor harmful, and may be negative which means that the piece is not just unprofitable, but even harmful (for environment).
  The farmer thinks that he has too much harmful garbage, so he wants to decrease the heap size, leaving a rectangular nonempty parallelepiped of smaller size cut of the original heap to maximize the sum of the values of the garbage pieces in it. You have to find the optimal parallelepiped value. (Actually, if all smaller parallelepiped
has value less than the original one, the farmer will leave the original parallelepiped).

题意

  求三维最大子立方体和

解法

DP
  这道题是 UVA1330 的升级版嘛……推荐在做这道题之前,先做 UVA1330Citygame ,这是我写的题解:http://blog.csdn.net/qq_38064038/article/details/78195045
  在 City game 的题解里,提到了一道题:黄金矿工——二维最大子矩阵和,加上本题的三维最大子立方体和,因此我们想到,有没有一维最大子矩阵和呢?
  答案是肯定的……只是一维的叫做最大子段和
  我们从一维开始分析,逐步地求解:
  我们举一个例子:设集合 S ={9,2,-6,2},显然这个集合的最大子段和就是11,接下来我们看得到过程
  从1开始枚举,记录之前的答案为sum,当我们枚举到第 k 位时,将sum加上 Sk ,然后更新答案,接着进行判断,如果 sum0 了,那么就将 ans 重置为0,即重新开始计数。原因很简单,设后面一截的和为 x ,因为我们是要求最大子段和,而此时sum0,所以 xsum+x ,因此摒弃掉前面的和会更优,下面是代码,复杂度O( n

  接下来我们看怎么求二维最大子矩阵和
  二维最大子矩阵和是一维最大子段和的拓展,因此我们猜想两者之间存在联系
  如果穷举所有子矩阵,有n4个子矩阵,一一枚举显然会超时,考虑将其与一维最大子段和的联系,我们可以枚举列的范围,然后降维,将问题转化为一维最大子段和
  求出给定矩阵每一行的前缀和,然后枚举列的起点 i 与终点j,选取的子矩阵的宽度为 ji+1 ,那么我们可以计算出第 k 行内ij的和,因此第二维(即列)变为了1,等同于一维(只有行),然后我们按照一维最大子段和的求法求出这一段的最大子矩阵和即可,下面是代码,复杂度O( n3

  接下来就是三维最大子立方体和
  同样的道理:既然二维与一维存在关系,所以三维也应该和二维存在关系
  按照思路,我们首先要将三维降维变成二维,假设我们压缩的一维是 x 方向,那么问题就变成了在yz平面上求二维最大子矩阵和,然后我们在压缩一维 y 方向,问题就变为了z方向上的一维最大子段和,直接求即可,下面是代码,复杂度O( n5

复杂度

O( n5

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define Lint long long int
using namespace std;
const Lint INF=1e18;
Lint w[21][21][21],s[21][21][21];
Lint f[21][21];
int m,n,o,T;
Lint ans;
int main()
{
    Lint sum;
    scanf("%d",&T);
    while( T-- )
    {
        ans=-INF;
        scanf("%d%d%d",&n,&m,&o);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                for(int k=1;k<=o;k++)
                    scanf("%lld",&w[i][j][k]);
        for(int i=1;i<=n;i++)   for(int j=1;j<=m;j++)   for(int k=1;k<=o;k++)   s[i][j][k]=s[i-1][j][k]+w[i][j][k];
        for(int i=1;i<=n;i++)
            for(int j=i;j<=n;j++)
                for(int x=1;x<=m;x++)
                {
                    for(int k=1;k<=o;k++)   f[x][k]=s[j][x][k]-s[i-1][x][k];
                    for(int y=x;y<=m;y++)
                    {
                        if( y>x )
                            for(int k=1;k<=o;k++)   f[y][k]=f[y-1][k]+s[j][y][k]-s[i-1][y][k];
                        sum=0;
                        for(int k=1;k<=o;k++)
                        {
                            sum+=f[y][k];
                            ans=max( ans,sum );
                            if( sum<0 )   sum=0;
                        }
                    }
                }
        printf("%lld\n",ans);
        if( T )   printf("\n");
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值