1. 项目背景与需求解析
"P1003铺地毯"这个题目源自NOIP(全国青少年信息学奥林匹克联赛)的一道经典模拟题。作为算法竞赛中的基础题型,它考察的是选手对二维平面坐标系的处理能力,以及如何高效模拟现实场景的编程思维。
这道题的核心场景是:在一个矩形房间内,依次铺设N张不同尺寸的地毯。每张地毯都有明确的铺设位置(左下角坐标)和尺寸(长宽)。当所有地毯铺设完成后,需要快速回答某个指定点(x,y)最上层覆盖的是哪张地毯。
注意:在实际竞赛中,这类题目往往有严格的时间限制,因此算法的时间复杂度是关键考量因素。直接遍历所有地毯的暴力解法虽然简单,但在大数据量时可能无法通过时间测试。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题建模与数据结构设计
2.1 输入输出规范
标准的题目输入通常包含:
- 第一行:整数N,表示地毯数量
- 接下来N行:每行四个整数a,b,g,k,表示地毯左下角坐标(a,b),以及横向长度g和纵向长度k
- 最后一行:两个整数x,y,表示查询点的坐标
输出要求:
- 一个整数,表示覆盖该点的最上层地毯编号(从1开始)
- 若无地毯覆盖,输出-1
2.2 数据结构选择
最直观的存储方式是使用结构体数组:
cpp复制struct Carpet {
int a, b; // 左下角坐标
int g, k; // 长宽尺寸
};
vector<Carpet> carpets(N);
这种存储方式的空间复杂度是O(N),完全在可接受范围内。对于查询操作,我们需要从最后铺设的地毯开始逆序检查(因为后铺的地毯会覆盖先铺的),直到找到第一个包含目标点的地毯。
3. 核心算法实现
3.1 点包含判断算法
判断点(x,y)是否在地毯内的逻辑是:
cpp复制bool isCovered(int x, int y, const Carpet& c) {
return x >= c.a && x <= c.a + c.g
&& y >= c.b && y <= c.b + c.k;
}
这个判断看似简单,但有几点需要注意:
- 边界条件处理:题目通常说明坐标是整数,且地毯边界也算覆盖区域(即包含等于的情况)
- 坐标系理解:注意题目给出的(a,b)是左下角坐标,而不是常见的左上角坐标系
3.2 完整查询流程
cpp复制int query(int x, int y, const vector<Carpet>& carpets) {
for (int i = carpets.size() - 1; i >= 0; --i) {
if (isCovered(x, y, carpets[i])) {
return i + 1; // 地毯编号从1开始
}
}
return -1;
}
这个逆向遍历的算法时间复杂度是O(N)最坏情况,对于题目常见的N≤10,000的数据规模完全足够。
4. 优化思路与进阶探讨
4.1 可能的优化方向
虽然O(N)的解法已经足够通过基础测试,但在实际工程应用中,我们可以考虑以下优化:
- 空间索引:使用二维线段树或R树等空间索引结构,可将查询时间优化到O(logN)
- 分层处理:如果查询点分布有规律,可以预处理各层的覆盖区域
- 并行计算:对于超大规模数据,可以将地毯分区后并行处理
4.2 竞赛中的常见变种
在实际比赛中,这道题可能有以下变种:
- 查询多个点而非单个点
- 动态添加和移除地毯
- 地毯可能有透明/半透明属性,需要计算叠加效果
- 非矩形地毯(如圆形、多边形)
5. 完整代码实现与测试
5.1 C++参考实现
cpp复制#include <iostream>
#include <vector>
using namespace std;
struct Carpet {
int a, b, g, k;
};
int main() {
int N;
cin >> N;
vector<Carpet> carpets(N);
for (int i = 0; i < N; ++i) {
cin >> carpets[i].a >> carpets[i].b >> carpets[i].g >> carpets[i].k;
}
int x, y;
cin >> x >> y;
int result = -1;
for (int i = N - 1; i >= 0; --i) {
if (x >= carpets[i].a && x <= carpets[i].a + carpets[i].g &&
y >= carpets[i].b && y <= carpets[i].b + carpets[i].k) {
result = i + 1;
break;
}
}
cout << result << endl;
return 0;
}
5.2 测试用例设计
好的测试用例应该包含:
- 基础用例:1-2张地毯的简单情况
- 边界情况:点正好在地毯边缘
- 多层覆盖:多张地毯重叠区域
- 无覆盖情况:点在所有地毯之外
- 极端数据:最大数量的地毯(如10,000张)
示例测试用例:
code复制3
1 0 2 3
0 2 3 3
2 1 3 3
2 2
预期输出:3(因为第三张地毯最后铺设,覆盖了(2,2)点)
6. 实际应用与扩展思考
虽然这个问题看起来是纯算法题目,但其核心思想在实际工程中有广泛应用:
- 图形界面开发:判断鼠标点击落在哪个UI组件上
- 地理信息系统:查询某坐标点所属的区域(如行政区划)
- 游戏开发:碰撞检测和物体拾取
- CAD软件:选择设计图中的特定元素
在解决这类问题时,我个人的经验是:
- 先明确坐标系和边界条件的定义
- 对于静态数据,逆向遍历是最简单有效的方案
- 当性能成为瓶颈时,才考虑更复杂的空间索引结构
- 测试时要特别注意边缘情况,如点正好在地毯边界上
