题目描述
由于John建造了牛场围栏,激起了奶牛的愤怒,奶牛的产奶量急剧减少。为了讨好奶牛,John决定在牛场中建造一个大型浴场。但是John的奶牛有一个奇怪的习惯,每头奶牛都必须在牛场中的一个固定的位置产奶,而奶牛显然不能在浴场中产奶,于是,John希望所建造的浴场不覆盖这些产奶点。这回,他又要求助于Clevow了。你还能帮助Clevow吗?
John的牛场和规划的浴场都是矩形。浴场要完全位于牛场之内,并且浴场的轮廓要与牛场的轮廓平行或者重合。浴场不能覆盖任何产奶点,但是产奶点可以位于浴场的轮廓上。
Clevow当然希望浴场的面积尽可能大了,所以你的任务就是帮她计算浴场的最大面积。
输入格式
输入文件的第一行包含两个整数L和W,分别表示牛场的长和宽。文件的第二行包含一个整数n,表示产奶点的数量。以下n行每行包含两个整数x和y,表示一个产奶点的坐标。所有产奶点都位于牛场内,即:0<=x<=L,0<=y<=W。
输出格式
输出文件仅一行,包含一个整数S,表示浴场的最大面积。
输入输出样例
输入 #1复制
10 10
4
1 1
9 1
1 9
9 9
输出 #1复制
80
说明/提示
0<=n<=5000
1<=L,W<=30000
Winter Camp 2002
感谢 @凯瑟琳98 提供了4组hack数据
思路:
详见论文:https://www.cnblogs.com/lxyyyy/p/11376224.html#_label3_0_4_0
本题坏点少,格子多,所以使用扫描法(算法一)。
大致思想是枚举一个左边界点,然后向右扫描,更新上边界和下边界。
但是这样会漏掉以整个矩形左边为左边界的情况,所以你要枚举右端点然后再向左扫描。
还会漏掉同时以矩阵左边为左边界,矩阵右边为右边界的情况,所以你要按纵坐标排序然后再枚举相邻两个点对应的极大子矩阵。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 5005;
struct Point {
int x, y;
} a[maxn];
int cmp1(Point a, Point b) {
if (a.x != b.x) return a.x < b.x;
return a.y < b.y;
}
int cmp2(Point a, Point b) {
return a.y < b.y;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
int k;
scanf("%d", &k);
for (int i = 1; i <= k; i++) {
scanf("%d%d", &a[i].x, &a[i].y);
}
a[++k] = {0, 0};
a[++k] = {0, m};
a[++k] = {n, 0};
a[++k] = {n, m};
sort(a + 1, a + 1 + k, cmp1);
int ans = 0;
for (int i = 1; i <= k; i++) {
int up = m, down = 0, len = n - a[i].x;
for (int j = i + 1; j <= k; j++) {
if (len * (up - down) <= ans) break;
ans = max(ans, (a[j].x - a[i].x) * (up - down));
if (a[j].y == a[i].y) break;
else if (a[j].y > a[i].y) {
up = min(up, a[j].y);
} else {
down = max(down, a[j].y);
}
}
up = m, down = 0, len = a[i].x;
for (int j = i - 1; j >= 1; j--) {
if (len * (up - down) <= ans) break;
ans = max(ans, (a[i].x - a[j].x) * (up - down));
if (a[j].y == a[i].y) break;
else if (a[j].y > a[i].y) {
up = min(up, a[j].y);
} else {
down = max(down, a[j].y);
}
}
}
sort(a + 1, a + 1 + k, cmp2);
for (int i = 1; i < k; i++) {
ans = max(ans, n * (a[i + 1].y - a[i].y));
}
printf("%d\n", ans);
return 0;
}