CSP历年复赛题-P1047 [NOIP2005 普及组] 校门外的树
原题链接:https://www.luogu.com.cn/problem/P1047
题意解读:0~l距离,每米一个点,包括两端,给定若干个区间,将区间内的点移除,最后剩下多个个点。
解题思路:
本题给出两种方法
1、本题数据规模不大,可以直接通过hash数组来处理
2、拓展一下,如果数据规模达到10^9,hash方法就不适用,可以采用区间合并
下面具体展开介绍。
方法1:hash
该法比较直接,设定int h[N],将所有区间中的点对应的h[i]设为1,最后统计h中还有多少0即可。
100分代码:
#include <bits/stdc++.h>
using namespace std;
int l, m, ans;
int h[100005];
int main()
{
cin >> l >> m;
int x, y;
for(int i = 1; i <= m; i++)
{
cin >> x >> y;
for(int j = x; j <= y; j++)
h[j] = 1;
}
for(int i = 0; i <= l; i++) //从0开始,0也是有效位置
{
if(h[i] == 0) ans++;
}
cout << ans;
return 0;
}
方法2:区间合并
由于多个区间有可能重叠,可以将所有区间进行合并,合并之后的区间没有重叠,然后用总数量减去所有区间覆盖的点数即可。
情况1:如上图所示,有三个区间[x1,y1],[x2,y2],[x3,y3],最终合并成[x1,y2],[x3,y3]两个区间,剩下的点数为L+1-(y3-x3+1)-(y2-x1+1)
情况2:如上图所示,有三个区间[x1,y1],[x2,y2],[x3,y3],最终合并成[x1,y1],[x3,y3]两个区间,剩下的点数为L+1-(y3-x3+1)-(y1-x1+1)
合并过程如下:
1、用结构体表示一个区间struct range{int x, y}
2、将所有区间按左端点从小到大排序
3、设定两个变量lastx,lasty表示上一个待保存的区间,初始设为第一个区间的左右端点
4、从第二区间开始枚举每一个区间,对于枚举到的区间,
如果左端点大于lasty,表示开始了一个新的区间,将lastx,lasty区间保存到vector,更新lastx、lasty为当前区间
如果左端点小于lasty,表示当前区间要和上一个区间合并,如果当前区间右端点更远则将lasty=当前区间的右端点
5、最后把剩下的lastx,lasty区间保存
具体过程可参考代码:
100分代码:
#include <bits/stdc++.h>
using namespace std;
int l, m, ans;
struct range
{
int x, y;
};
vector<range> r1; //合并前的区间
vector<range> r2; //合并后的区间
bool cmp(range a, range b)
{
return a.x < b.x;
}
int main()
{
cin >> l >> m;
int x, y;
for(int i = 1; i <= m; i++)
{
cin >> x >> y;
r1.push_back({x, y});
}
sort(r1.begin(), r1.end(), cmp);
int lastx = r1[0].x, lasty = r1[0].y; //lastx,lasty为第一个区间
for(int i = 1; i < r1.size(); i++) //从第二个区间开始枚举
{
if(r1[i].x > lasty) //左端点大于lasty,表示开始了一个新的区间
{
r2.push_back({lastx, lasty}); //将lastx,lasty区间保存
lastx = r1[i].x, lasty = r1[i].y; //更新lastx,lasty为当前区间
}
else lasty = max(lasty, r1[i].y); //进行区间合并,如果当前区间右端点更远,将上一个区间右端点更新为当前区间右端点
}
r2.push_back({lastx, lasty}); //把剩下的lastx,lasty区间保存
ans = l + 1;
for(int i = 0; i < r2.size(); i++)
{
ans -= (r2[i].y - r2[i].x + 1);
}
cout << ans;
return 0;
}