poj1964
题意:给出一个格子矩阵,格子分为两种——可用和不可用。要求在画一个矩形,矩形所覆盖的格子全都是可用的,并且要求矩形的面积最大。
分析:dp。O(n^2)的效率。先说一种低效的方法,依次枚举每个格子, 从该格子向上走直到遇到不可用点为止,将这段路程对应的线段,向左平移直到遇到不可用的格子为止,再向右平移直到遇到不可用的格子为止。这个想段所覆盖过的面积是一个矩形,计算这个矩形面积并更新结果。这样做一定可以找到正确答案,因为对于最大的那个矩形一定有一个挡住它向上延伸的不可用的格子,当我们从这个格子正下方的那个矩形下边缘上的格子出发向上走进行上述操作即可获得这个矩形。对于这种低效的方法如果获取每个格子能向上走的距离的效率是O(1)的,获取这个线段左右平移的距离的效率也是O(1)的,那么总体效率就是O(n^2)的,因为只有n^2个格子。显然每个格子可以向上走多远,是可以通过简单的每列由上向下的递推得到的。而在已知一行所有格子向上走的线段的高度的条件下,是可以通过单调栈的方式来确定每个线段左侧距离最近的比它短的线段在哪里的。单调栈可以以O(n)的效率计算出n个线段的结果,相当于每个线段O(1)。右侧同理。
#include <stack> #include <cstdio> #include <cstring> using namespace std; #define MAX_WIDTH 1005 #define MAX_HEIGHT 1005 struct Elem { int pos, value; Elem() {} Elem(int pos, int value):pos(pos), value(value) {} }; int height, width; int area[MAX_HEIGHT][MAX_WIDTH]; int max_height[MAX_WIDTH]; int left[MAX_WIDTH], right[MAX_WIDTH]; char next_ch() { char ch; do ch = getchar(); while (ch != 'F' && ch != 'R'); return ch; } void input() { scanf("%d%d", &height, &width); for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) if (next_ch() == 'F') area[i][j] = 1; else area[i][j] = 0; } void work() { stack<Elem> stk; memset(max_height, 0, sizeof(max_height)); int ans = 0; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) if (area[i][j]) max_height[j]++; else max_height[j] = 0; stk.push(Elem(-1, -1)); for (int j = 0; j < width; j++) { while (stk.top().value >= max_height[j]) stk.pop(); left[j] = j - stk.top().pos; stk.push(Elem(j, max_height[j])); } while (!stk.empty()) stk.pop(); stk.push(Elem(width, -1)); for (int j = width - 1; j >= 0; j--) { while (stk.top().value >= max_height[j]) stk.pop(); right[j] = stk.top().pos - j; stk.push(Elem(j, max_height[j])); } while (!stk.empty()) stk.pop(); for (int j = 0; j < width; j++) ans = max(ans, max_height[j] * (left[j] + right[j] - 1)); } printf("%d\n", ans * 3); } int main() { int t; scanf("%d", &t); while (t--) { input(); work(); } return 0; }