P1034 [NOIP 2002 提高组] 矩形覆盖 题解
题意简析
题目要求用 \(k\) 个边平行于坐标轴的矩形覆盖平面上给定的 \(n\) 个点,且矩形必须完全分开(边线和顶点都不能重合)。目标是使所有矩形的面积之和最小。覆盖单个点或共线点的矩形面积为 \(0\)。
思路解析
注意到 \(1 \le n \le 50\),那么我们暴力搜索加或不加剪枝就可以过了。
剪枝
-
将点按 \(x\) 坐标(\(x\) 相同按 \(y\))排序。
-
当前总面积超过已知最小面积时回溯。
-
若点在矩形内部,加入时不改变边界,直接递归。
代码实现
跑的飞快。
#pragma G++ optimize("O3", "unroll-loops", "omit-frame-pointer", "inline")
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 55;
const int MAXK = 5;
const int INF = 1000;
struct Point {
int x, y;
} pts[MAXN];
struct Rect {
int minx, maxx, miny, maxy;
};
int n, k, ans = INT_MAX;
Rect rects[MAXK];
bool isSeparate(const Rect& a, const Rect& b) {
return a.maxx < b.minx || a.minx > b.maxx || a.maxy < b.miny || a.miny > b.maxy;
}
void dfs(int idx, int area, int rect_cnt) {
if (area >= ans)
return;
if (idx == n) {
ans = min(ans, area);
return;
}
for (int i = 0; i < rect_cnt; i++) {
Rect old = rects[i];
int x = pts[idx].x, y = pts[idx].y;
Rect now = rects[i];
now.minx = min(now.minx, x);
now.maxx = max(now.maxx, x);
now.miny = min(now.miny, y);
now.maxy = max(now.maxy, y);
bool inside = false;
if (old.minx <= x && x <= old.maxx && old.miny <= y && y <= old.maxy) {
inside = true;
rects[i] = now;
dfs(idx + 1, area, rect_cnt);
rects[i] = old;
} else {
int old_area = (old.minx == INF) ? 0 : (old.maxx - old.minx) * (old.maxy - old.miny);
int new_area = (now.maxx - now.minx) * (now.maxy - now.miny);
int area_diff = new_area - old_area;
if (area + area_diff >= ans)
continue;
rects[i] = now;
bool valid = true;
for (int j = 0; j < rect_cnt; j++) {
if (i == j || rects[j].minx == INF)
continue;
if (!isSeparate(rects[i], rects[j])) {
valid = false;
break;
}
}
if (valid)
dfs(idx + 1, area + area_diff, rect_cnt);
rects[i] = old;
}
}
if (rect_cnt < k) {
Rect now = {pts[idx].x, pts[idx].x, pts[idx].y, pts[idx].y};
rects[rect_cnt] = now;
bool valid = true;
for (int i = 0; i < rect_cnt; i++) {
if (rects[i].minx == INF)
continue;
if (!isSeparate(rects[i], now)) {
valid = false;
break;
}
}
if (valid)
dfs(idx + 1, area, rect_cnt + 1);
rects[rect_cnt] = {INF, -INF, INF, -INF};
}
return;
}
int main() {
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> pts[i].x >> pts[i].y;
}
sort(pts, pts + n, [](const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
});
for (int i = 0; i < k; i++) {
rects[i] = {INF, -INF, INF, -INF};
}
dfs(0, 0, 0);
cout << ans << endl;
return 0;
}