ACM - ICPC World Finals 2013 I Pirate Chest
原题下载:http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf
题目翻译:
问题描述
海盗Dick受够了在公海上厮杀、抢劫、盗窃了,这把生活弄得一塌糊涂。所以他决定隐退,而且他已经找到了一座理想的小岛,只要钱没花完就能在那儿安度余生。他现在有很多金币,他想要把这些金币存在一个宝箱里(毕竟他还是个海盗)。Dick可以建造一个边长都是正整数的长方体宝箱,宝箱底面的长宽不能超过某个特定的尺寸,不过宝箱的高度可以是任意正整数。现在他需要找一个地方把宝箱藏起来。在探索小岛的过程中,他找到了一个好地方。
Dick打算通过把宝箱淹没在一个黑暗的池塘里来藏宝箱。池塘的表面是矩形的,它完全填满了一个山谷的底部,四周都是竖直的悬崖。Dick调查了这个池塘,他在池塘表面建立了平面直角坐标系的网格,并测得了每个单位方格的深度。当宝箱沉入水中时,它会一直下沉直到碰到池底。沉底时,宝箱的顶面会和池塘的表面平行,宝箱的边缘会和网格对齐。宝箱排开了一部分水,这会使池塘的水位上升(即使被宝箱排开的水没有空隙上升也会这样)。四周的悬崖足够高,所以水不会溅出来。当然,由于宝箱不能被别人看到,宝箱的顶面必须严格低于水面。你的任务就是求出Dick能藏下的宝箱的最大体积。
在下图中,左边的图表示池塘的形态,中间的图表示一种体积为3的放置方法,右边的图表示一种体积为4的放置方法。这也是能够藏下的最大体积。注意,如果右边的图的宝箱再变高1单位,它的顶面就能被看到了,因为此时它的顶面和水面一样高。
输入格式
第一行包含四个整数a, b, m, n,表示池塘表面的大小是m*n,宝箱底面一边尺寸不能超过a,另一边的尺寸不能超过b。另外,a和b满足底面为a*b的宝箱不能覆盖整个池塘。
接下来m行,每行n个整数di,j表示方格(i, j)的深度。
输出格式
第一行包含一个整数,表示能完全淹没在池塘里的满足要求的宝箱的最大体积。如果不存在能淹没在池塘里的宝箱,输出0。
样例输入
3 1 2 3
2 1 1
2 2 1
样例输出
4
样例输入
4 1 1 5
2 0 2 2 2
样例输出
12
样例输出
2 3 3 5
2 2 2 2 2
2 2 2 2 2
2 2 2 2 2
样例输出
18
数据规模和约定
1≤a, b, m, n≤500,0≤di,j≤109。
题目大意:
有一个\(m\times n\)的池塘,池塘里面是凹凸不平的,第(i,j)个格有一个深度\(d_{ij}\),要求制作一个箱子(尺寸都是正整数),箱子的两底长分别小于a, b。将其水平放置在池塘底部,箱子会排开与它相等体积的水(但不会溢出),要求保证排开水后箱子的顶部必须严格小于水面,求箱子的最大体积
思路分析:
这道题一上来的感觉有点像之前学过的最大子矩形,一开始的思路是枚举箱子的高度,然后按最大子矩形的搞一搞,但是考虑到数据范围,这样做一定会超时。看一看底边长的范围,感觉复杂度还是\(O(n^3)\)。直接枚举底边位置和边长是\(O(n^4)\)的,我们要想办法给问题降维。通过枚举某一条底边界所在的行,并枚举另一条底边界的长度,可以将任务限制在一个竖直剖面上,然后我们发现单调性之后就可以用一个单调栈维护来求出剖面上的最大矩形,就可以把复杂度做到\(O(n^3)\)了
(今天累了。。。没有力气写得更细致了。。抱歉)
参考代码:
1 //date 20140126 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 inline int getint() 6 { 7 int ans(0); char w = getchar(); 8 while(w < '0' || w > '9')w = getchar(); 9 while('0' <= w && w <= '9') 10 { 11 ans = ans * 10 + w - '0'; 12 w = getchar(); 13 } 14 return ans; 15 } 16 template <typename T>inline T max(T a, T b){return a > b ? a : b;} 17 template <typename T>inline T min(T a, T b){return a < b ? a : b;} 18 inline void swap(int &a, int &b){a ^= b ^= a ^= b;} 19 const int maxn = 505; 20 int a, b, n, m; 21 long long S; 22 int deep[maxn][maxn]; 23 int now[maxn], stack[maxn], len[maxn]; 24 int top; 25 int mex, tlen; 26 inline long long solve(int r, int c, int bottom) 27 { 28 long long h = (bottom * S - 1) / (S - r * c); 29 return h * r * c; 30 } 31 int main() 32 { 33 while(scanf("%d%d%d%d", &a, &b, &m, &n) != EOF) 34 { 35 S = (long long)m * n; 36 if(a > b)swap(a, b); //a < b 37 for(int i = 1; i <= m; ++i) 38 for(int j = 1; j <= n; ++j) 39 deep[i][j] = getint(); 40 long long ans = 0LL; 41 for(int i = 1; i <= m; ++i) 42 { 43 memset(now, 0x7F, sizeof now); 44 for(int j = i; j <= m && j < i + b; ++j) 45 { 46 if(j < i + a)mex = b; else mex = a; 47 for(int k = 1; k <= n; ++k)now[k] = min(now[k], deep[j][k]); 48 stack[top = 0] = -1; len[0] = 0; 49 for(int k = 1; k <= n; ++k) 50 { 51 tlen = 1; 52 while(stack[top] > now[k]) 53 { 54 ans = max(ans, solve(j - i + 1, min(len[top], mex), stack[top])); 55 if(now[k] < stack[top - 1])len[top - 1] += len[top]; 56 else tlen += len[top]; 57 --top; 58 } 59 stack[++top] = now[k]; len[top] = tlen; 60 } 61 while(top > 0) 62 { 63 ans = max(ans, solve(j - i + 1, min(len[top], mex), stack[top])); 64 len[top - 1] += len[top]; 65 --top; 66 } 67 } 68 } 69 printf("%I64d\n", ans); 70 } 71 return 0; 72 }
需要注意的地方:
求解可行高度时需要进行简单的计算,单调栈中注意还要始终维护当前的最大可行长度