题目链接

​P1578 奶牛浴场​


  • 题目大意:P1578 奶牛浴场_搜索的矩阵,有几个点(不是格子而是坐标点),问最大的没有覆盖这些点的矩阵。

  • 思路:首先搞清楚模型,(1,1)和(4,4)是右边的情况,因为给出的案例下面两种答案都是80。
  • P1578 奶牛浴场_搜索_02

  • 这题虽然不能用悬线法(矩阵太大不可能枚举点)来做,但是可以用类似悬线的思想来做。
    对答案子矩阵进行分析,四个边,如果没有被约束,则会往外扩展。所以,四个边一定被障碍点或大矩形边界约束,这个就有点极大的意思在里面了。
    先按P1578 奶牛浴场_搜索_03值排序。
    以一个点作为左边界,尽可能的往右延伸。遇到障碍点时调整上下界继续延伸。
  • P1578 奶牛浴场_i++_04

  • 考虑到右边界,需要用if判断或将大矩阵的右上和右下放入点集。
    但是考虑不到下图中的情况,所以需要以每个点为右边界,做一遍往左延伸。
  • P1578 奶牛浴场_搜索_05

  • 最后一种横穿的情况,只需要再次按P1578 奶牛浴场_i++_06值排序遍历即可。
  • P1578 奶牛浴场_搜索_07

  • 有几个剪枝:
  • 往右延伸的时候,假设按照当前的上下界,即使延伸到右边界都不能超过答案就break;

  • 如果遇到和初始点相同的y值时,按理来说会被分解成两个矩形。但是这两个矩形可以证明一定会被除当前搜索外的情况下考虑。下图中,红色部分在右边界障碍点的左延中被考虑,黄色部分在最后按y值排序中,会得到比之更大的解。
  • P1578 奶牛浴场_i++_08

  • 参考代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
int n, m;
struct node {
int x, y;
bool operator<(const node &rhs) const { return x < rhs.x; }
} e[N];
bool cmp(const node &a, const node &b) { return a.y < b.y; }
int main() {
scanf("%d%d", &n, &m);
int c;
scanf("%d", &c);
for (int i = 1; i <= c; i++) {
scanf("%d%d", &e[i].x, &e[i].y);
}
e[++c].x = 0, e[c].y = 0;
e[++c].x = n, e[c].y = 0;
e[++c].x = n, e[c].y = m;
e[++c].x = 0, e[c].y = m;
sort(e + 1, e + 1 + c);

int ans = 0, u, d, l, r;
for (int i = 1; i <= c; i++) {
u = m, d = 0, l = e[i].x;
for (int j = i + 1; j <= c; j++) {
r = e[j].x;
ans = max(ans, (r - l) * (u - d));
if (e[j].y == e[i].y) // 同y值
break;
if (e[j].y < u && e[j].y > d) { // 调整上下界
if (e[j].y > e[i].y)
u = e[j].y;
else
d = e[j].y;
}
if ((u - d) * (n - l) <= ans) // 不能得到更优解
break;
}

u = m, d = 0, r = e[i].x;
for (int j = i - 1; j >= 1; j--) {
l = e[j].x;
ans = max(ans, (r - l) * (u - d));
if (e[j].y == e[i].y)
break;
if (e[j].y < u && e[j].y > d) {
if (e[j].y > e[i].y)
u = e[j].y;
else
d = e[j].y;
}
if ((u - d) * (r - 0) <= ans)
break;
}
}
sort(e + 1, e + 1 + c, cmp);
for (int i = 1; i < c; i++) {
ans = max(ans, (e[i + 1].y - e[i].y) * n);
}
printf("%d\n", ans);
}