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

首先看到本题的数据范围:$ 1 \leq n \leq 50, 1\leq k \leq 4$,数量非常少。

如果暴力将每个点加入几个不同的集合中,即使对于 \(n \leq 50\) 这种极小的数据也是指数级的(当 \(k \leq 4,n=50\) 时,最坏复杂度达到了 \(4^{50}\),无法通过)。

于是我们考虑使用搜索进行操作。

条件:

  • 矩形边必须平行于坐标轴;
  • 各矩形必须完全分开(边线和顶点都不能重合);
  • 覆盖单个点或共线点的矩形面积为 \(0\)

1. 状态表示

  • 用四元组表示矩形:\((x_1, y_1, x_2, y_2, c)\)
  • \((x_1,y_1)\) 为左下角,\((x_2,y_2)\) 为右上角,\(c\) 为包含点数。

2. DFS 框架

void dfs(int u) {  // 处理第 u 个点
    if (u == n + 1) {
        计算所有矩形面积和,更新最优答案;
        return;
    }
    
    for (int i = 0; i < k; i++) {  // 尝试加入第 i 个矩形
        保存第 i 个矩形的当前状态;
        将第 u 个点加入第 i 个矩形;
        更新矩形边界;
        
        if (检查所有矩形互不相交) {
            dfs(u + 1);  // 继续处理下一个点
        }
        
        恢复第 i 个矩形状态;  // 回溯
    }
}

加入点操作

if (矩形为空) {
    x1 = x2 = 点的 x 坐标;
    y1 = y2 = 点的 y 坐标;
    c = 1;
} else {
    x1 = min(x1, 点的 x 坐标);
    y1 = min(y1, 点的 y 坐标);
    x2 = max(x2, 点的 x 坐标);
    y2 = max(y2, 点的 y 坐标);
    c++;
}

相交判断
两矩形 A,B 不相交当且仅当满足以下条件之一:

  • A 在 B 的左边:\(A_{x_2} < B_{x_1}\)
  • A 在 B 的右边:\(A_{x_1} > B_{x_2}\)
  • A 在 B 的下边:\(A_{y_2} < B_{y_1}\)
  • A 在 B 的上边:\(A_{y_1} > B_{y_2}\)
bool flag = (A.x2 < B.x1) || (B.x2 < A.x1) || 
           (A.y2 < B.y1) || (B.y2 < A.y1);

面积计算

int calc(int i) {
    if (r[i].c <= 1) return 0;  // 单点或空矩形
    return (r[i].x2 - r[i].x1) * (r[i].y2 - r[i].y1);
}

最后是你们最爱的完整答案:

#include <bits/stdc++.h>

#define N 55
#define INF 0x3f3f3f3f

using namespace std;

inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

inline void write(int x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

inline void writeln(int x) {
    write(x);
    putchar('\n');
}

struct point {
    int x, y;
} p[N];

struct node {
    int x1, y1, x2, y2;
    int c;
} r[5];

int n, k, ans;

int calc(int i) {
    if (r[i].c <= 1) return 0;
    return (r[i].x2 - r[i].x1) * (r[i].y2 - r[i].y1);
}

bool chk() {
    for (int i = 0; i < k; i++) {
        for (int j = i + 1; j < k; j++) {
            if (r[i].c > 0 && r[j].c > 0) {
                if (!(r[i].x2 < r[j].x1 || r[j].x2 < r[i].x1 || 
                      r[i].y2 < r[j].y1 || r[j].y2 < r[i].y1)) {
                    return false;
                }
            }
        }
    }
    return true;
}

void dfs(int u) {
    if (u == n + 1) {
        int s = 0;
        for (int i = 0; i < k; i++) {
            s += calc(i);
        }
        ans = min(ans, s);
        return;
    }
    
    for (int i = 0; i < k; i++) {
        node t = r[i];
        
        if (r[i].c == 0) {
            r[i].x1 = r[i].x2 = p[u].x;
            r[i].y1 = r[i].y2 = p[u].y;
            r[i].c = 1;
        } else {
            r[i].x1 = min(r[i].x1, p[u].x);
            r[i].y1 = min(r[i].y1, p[u].y);
            r[i].x2 = max(r[i].x2, p[u].x);
            r[i].y2 = max(r[i].y2, p[u].y);
            r[i].c++;
        }
        
        if (chk()) {
            dfs(u + 1);
        }
        
        r[i] = t;
    }
}

int main() {
    n = read();
    k = read();
    
    for (int i = 1; i <= n; i++) {
        p[i].x = read();
        p[i].y = read();
    }
    
    ans = INF;
    dfs(1);
    
    writeln(ans);
    
    return 0;
}
posted @ 2025-10-14 23:39  amlhdsan  阅读(2)  评论(0)    收藏  举报