P1034 [NOIP 2002 提高组] 矩形覆盖 题解

题意简析

题目要求用 \(k\) 个边平行于坐标轴的矩形覆盖平面上给定的 \(n\) 个点,且矩形必须完全分开(边线和顶点都不能重合)。目标是使所有矩形的面积之和最小。覆盖单个点或共线点的矩形面积为 \(0\)

思路解析

注意到 \(1 \le n \le 50\),那么我们暴力搜索加或不加剪枝就可以过了。

剪枝

  1. 将点按 \(x\) 坐标(\(x\) 相同按 \(y\))排序。

  2. 当前总面积超过已知最小面积时回溯。

  3. 若点在矩形内部,加入时不改变边界,直接递归。

代码实现

跑的飞快。

#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;
}
posted @ 2025-07-31 23:02  TangyixiaoQAQ  阅读(13)  评论(0)    收藏  举报