题面
??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的升级版嘛……推荐在做这道题之前,先做【UVA1330】Citygame,这是我写的题解:http://blog.csdn.net/qq_38064038/article/details/78195045
??在City?game的题解里,提到了一道题:黄金矿工——二维最大子矩阵和,加上本题的三维最大子立方体和,因此我们想到,有没有一维最大子矩阵和呢?
??答案是肯定的……只是一维的叫做最大子段和
??我们从一维开始分析,逐步地求解:
??我们举一个例子:设集合S={9,2,-6,2},显然这个集合的最大子段和就是11,接下来我们看得到过程
从1开始枚举,记录之前的答案为sum ,当我们枚举到第k位时,将sum 加上Sk,然后更新答案,接着进行判断,如果sum<0了,那么就将ans重置为0,即重新开始计数。原因很简单,设后面一截的和为x,因为我们是要求最大子段和,而此时sum<0 ,所以x>sum+x,因此摒弃掉前面的和会更优,下面是代码,复杂度O(n)
接下来我们看怎么求二维最大子矩阵和
二维最大子矩阵和是一维最大子段和的拓展,因此我们猜想两者之间存在联系
如果穷举所有子矩阵,有n4 个子矩阵,一一枚举显然会超时,考虑将其与一维最大子段和的联系,我们可以枚举列的范围,然后降维,将问题转化为一维最大子段和
??求出给定矩阵每一行的前缀和,然后枚举列的起点i与终点j ,选取的子矩阵的宽度为j?i+1,那么我们可以计算出第k行内【i,j】 的和,因此第二维(即列)变为了1,等同于一维(只有行),然后我们按照一维最大子段和的求法求出这一段的最大子矩阵和即可,下面是代码,复杂度O(n3)
??接下来就是三维最大子立方体和
??同样的道理:既然二维与一维存在关系,所以三维也应该和二维存在关系
??按照思路,我们首先要将三维降维变成二维,假设我们压缩的一维是x方向,那么问题就变成了在y?z 平面上求二维最大子矩阵和,然后我们在压缩一维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;
}