洛谷P1003 [NOIP2011 提高组]铺地毯题解

该博客介绍了如何解决NOIP2011提高组中关于铺地毯的问题。通过暴力模拟的方法,以O(n)的时间复杂度求解。首先存储地毯位置,然后逆序枚举找出覆盖指定点的最上层地毯编号。文章提供了一个样例解释,并展示了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暴力模拟,时间复杂度只有 O(n)

题目描述

为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯。一共有n张地毯,编号从1到n。现在将这些地毯按照编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。

地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的编号。注意:在矩形地毯边界和四个顶点上的点也算被地毯覆盖。

整体思路

先将地毯的位置都存储下来。地毯是从低到高叠放的,要知道最上面的地毯编号,逆序依次枚举就行了。

蒟个栗子

样例输入:
3
1 0 2 3
0 2 3 3
2 1 3 3
2 2

拿样例来说,共3张地毯,坐标分别是:

[ ( 1 , 0 ) , ( 3 , 3 ) ] , [ ( 0 , 2 ) , ( 3 , 5 ) ] 和 [ ( 2 , 1 ) , ( 5 , 4 ) ]。

逆序枚举,先检查第3张地毯,点 ( 2 , 2 ) 在 [ ( 2 , 1 ) , ( 5 , 4 ) ] 上,所以直接输出3。

样例图片

代码实现看这里

#include<iostream>
using namespace std;
int a[10005]<
<think>嗯,用户想了解的P1003铺地毯问题,我得先回忆一下这个题目的内容。题目大意是给出n块地毯的信息,每块地毯的左下角坐标和长宽,然后确定某一点(x,y)最上面覆盖的是哪块地毯。如果没有被覆盖,就输出-1。这个问题应该属于模拟题,直接按照题意处理就行。 首先,我得考虑如何高效地解决这个问题。因为地毯是按顺序铺的,后面的会覆盖前面的,所以只需要逆序遍历所有地毯,找到第一个覆盖目标点的即可。这样一旦找到,就可以立即返回结果,不需要继续检查前面的地毯,节省时间。 然后,输入的处理需要注意。每个地毯的坐标是左下角,加上在x和y方向的长度。比如,左下角(a,b),x方向长度g,y方向长度k,那么地毯覆盖的范围应该是a到a+g,b到b+k。不过这里有个问题,坐标的范围是否包含边界点?比如,当x等于a+g或者y等于b+k的时候,是否算在覆盖范围内?根据题目描述,应该是左闭右开吗?或者题目可能给出的是地毯覆盖的具体四个顶点,比如左下角是(a,b),右上角是(a+g, b+k),这时候判断点是否在闭区间内。例如,x >=a 且 x <=a+g,y >=b 且 y <=b+k。这个需要仔细看题目描述。 比如,题目给出的应该是,地毯覆盖的坐标范围是从左下角(a,b)开始,向右延伸g,向上延伸k。所以右上角的坐标应该是(a + g, b + k)。所以点(x,y)满足a <=x <=a+g 和 b <=y <=b+k时,就认为被覆盖了。或者可能是否包括等于的情况?比如,题目中的例子可能给出具体的测试用例,这时候要看样例。比如,假设样例输入中有某个情况,这时候需要正确判断。 比如,假设地毯左下角是(1,1),g=2,k=2,那么覆盖的区域是x从1到3(1+2),y从1到3。那么点(3,3)是否被覆盖?这时候要看题目描述中的条件。通常在这种问题中,边界点是算在内的。所以判断条件是x >=a,x <=a+g,y >=b,y <=b +k。这样,包括右上角的点。 然后,如何存储地毯的信息?可以用一个结构体数或者二维数存储每个地毯的四个参数:a, b, g, k。然后对每个地毯,判断该点是否在其覆盖范围内。因为n可以达到1e4,所以时间复杂度是O(n),这在C++中是可行的。 接下来,用户可能需要具体的实现步骤。比如,读取n,然后读取n个地毯的信息,存储起来。然后读取目标点(x,y),逆序检查每个地毯是否覆盖该点。一旦找到,输出该地毯的编号(从1开始),否则输出-1。 需要注意的是,地毯的编号是按输入顺序排列的,所以第i个地毯的编号是i+1?或者编号是从1到n?比如,输入的第一个地毯是编号1,第二个是2,依此类推。所以当逆序遍历时,从n-1到0索引的话,对应的编号是i+1。假设数从0开始存储的话。 例如,假设数存储为0到n-1,对应编号1到n。所以当逆序检查的时候,找到第一个满足条件的,就输出i+1。 现在,具体到代码实现。代码的大致结构应该是这样的: 读取n; 循环n次,读取a, b, g, k,保存到数; 读取x, y; 从最后一个地毯开始往前遍历,检查当前地毯是否覆盖(x,y); 如果找到,输出编号(i+1),否则输出-1。 关键点在于如何判断点是否在地毯内。正确的条件是:x >=a && x <=a+g && y >=b && y <=b +k。对吗?或者是否应该用x在a到a+g之间,包括a,但不包括a+g?或者题目中的描述可能有不同的定义。 例如,题目描述中的地毯是“铺放”,其四个角的坐标是(a,b), (a+g, b), (a, b+k), (a+g, b+k),那么覆盖的区域应该是所有满足a<=x<=a+g,b<=y<=b+k的点。这时候判断条件是正确的。所以代码中的判断条件应该是: 如果x >=a && x <=a +g && y >=b && y <=b +k → 满足条件。 那在代码中,对于每个地毯的a, b, g, k,做这样的判断即可。 例如,样例输入: 3 1 0 2 3 0 2 3 3 2 1 3 3 然后查询点(2,2)。那么每个地毯的范围是: 第一个:a=1, g=2 → x从1到3;b=0, k=3 → y从0到3。所以点(2,2)在该地毯内。但后面是否有其他地毯覆盖? 第二个地毯:a=0,g=3 →x从0到3;b=2,k=3 →y从2到5。点(2,2)在此范围内吗?是的。所以第二个地毯也会覆盖这个点。但第三个地毯? 第三个地毯:a=2,g=3 →x从2到5;b=1,k=3 →y从1到4。点(2,2)符合,所以第三个地毯覆盖该点。所以最终输出3。 所以代码的逻辑是正确的,逆序遍历的话,第三个地毯是最后一个,所以会被选中。 这样,代码的逻辑是正确的。 接下来,考虑可能的边界情况。比如,点正好在某个地毯的边界上。例如,x=a,或者x=a+g,y=b或者y=b+k。这时候是否算作被覆盖?根据题目给出的样例,可能需要这样处理。所以代码中的条件需要包含等于的情况。 再比如,当g或k为0的情况?但题目中的输入数据应该不会有这种情况,因为题目中给出的g和k是“沿x轴方向和y轴方向的长度”,所以可能都是正整数。所以在代码中不需要处理这种情况,或者可以假设输入数据是有效的。 综上,代码的大致结构是: #include <iostream> using namespace std; struct Carpet { int a, b, g, k; }; int main() { int n; cin >> n; Carpet carpets[n]; // 或者用vector,但n最多是1e4,没问题。 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 ans = -1; for (int i = n-1; i >=0; i--) { Carpet& c = carpets[i]; if (x >= c.a && x <= c.a + c.g && y >= c.b && y <= c.b + c.k) { ans = i+1; // 因为地毯编号从1开始 break; } } cout << ans << endl; return 0; } 需要注意,这里使用C++的数,n如果很大,比如1e4的话,在栈上可能有问题,所以应该用动态分配或者全局数。例如,在C++中,如果n是一个变量,那么int carpets[n]是C99的变长数,可能不被所有编译器支持。比如,在C++标准中,数的大小必须是编译期常量。所以这段代码可能在某些编译器(如MSVC)中无法编译。正确的做法是使用动态数,比如用vector<Carpet> carpets(n); 或者在堆上分配。 例如,修改为: vector<Carpet> carpets(n); 或者用: Carpet *carpets = new Carpet[n]; 然后在循环中读取。最后delete[] carpets。不过对于编程竞赛来说,通常使用vector或者静态数,因为n的上限可能已知。例如,题目可能给出n的范围,比如n<=1e4,所以可以预先分配足够大的数。 比如,题目中的n范围是?比如,原题中的n可能最大到1e4,所以可以定义全局数: const int MAXN = 1e4 + 5; Carpet carpets[MAXN]; 这样在main函数中,读取n后,循环读取到n个元素即可。 所以正确的代码可能需要调整数的声明方式。比如: struct Carpet { ... }; int main() { int n; cin >> n; vector<Carpet> carpets(n); for (...) { ... } ... } 或者使用动态数。这样可以避免栈溢出的问题。例如,当n是1e4时,每个结构体是四个int,占16字节,总共有1e4*16=160,000字节,约160KB,这在栈上是允许的?可能在不同的编译环境中栈的大小不同。比如,在竞赛环境中,通常栈的大小可能较大,所以不会有问题。但为了安全起见,还是使用动态分配或者全局数比较好。 例如,在代码中将数声明为全局变量: #include <iostream> using namespace std; const int MAXN = 10005; struct Carpet { int a, b, g, k; } carpets[MAXN]; int main() { int n; cin >> 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 ans = -1; for (int i = n-1; i >=0; --i) { auto &c = carpets[i]; if (x >= c.a && x <= c.a + c.g && y >= c.b && y <= c.b + c.k) { ans = i+1; break; } } cout << ans << endl; return 0; } 这样应该不会有栈溢出的问题。 所以,用户的问题是关于这个题目的解法和C++代码实现。需要给出步骤和解释。 可能的错误点在于数的声明方式,或者在条件判断中的边界处理。例如,是否包含等于的情况。所以需要在解释中强调条件判断的正确性。 总结来说,解题思路是逆序检查每个地毯是否覆盖目标点,找到第一个满足条件的地毯编号。代码的时间复杂度是O(n),符合题目要求。</think>### 题目分析 P1003 铺地毯NOIP2011 提高的题目,考察模拟和逻辑判断能力。题目要求根据铺地毯的顺序,判断某一点最上层被哪块地毯覆盖。 ### 核心思路 1. **逆序检查**:由于后铺的地毯会覆盖先铺的,只需**逆序遍历**所有地毯,找到第一个覆盖目标点的即可。 2. **覆盖条件判断**:若目标点 $(x,y)$ 满足: $$ x \in [a_i, a_i + g_i] \quad \text{且} \quad y \in [b_i, b_i + k_i] $$ 则被第 $i$ 块地毯覆盖。 ### 代码实现 ```cpp #include <iostream> using namespace std; const int MAXN = 10005; struct Carpet { int a, b, g, k; } carpets[MAXN]; int main() { int n; cin >> 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 ans = -1; // 逆序检查地毯 for (int i = n - 1; i >= 0; --i) { const Carpet& c = carpets[i]; if (x >= c.a && x <= c.a + c.g && y >= c.b && y <= c.b + c.k) { ans = i + 1; // 地毯编号从1开始 break; } } cout << ans << endl; return 0; } ``` ### 关键点解释 1. **数据结构**:用结构体数存储每块地毯的左下角坐标 $(a, b)$ 和延伸长度 $(g, k)$。 2. **覆盖判断**:通过判断点 $(x, y)$ 是否在地毯的矩形区域内,注意边界包含等号(闭区间)。 3. **逆序优化**:从最后一块地毯倒序检查,确保找到最上层覆盖的地毯,直接跳出循环。 ### 复杂度分析 - **时间复杂度**:$O(n)$,遍历所有地毯一次。 - **空间复杂度**:$O(n)$,存储地毯信息。 此方法高效且直接,完美通过题目数据范围限制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值