题解 CF527D 【Clique Problem】

t162

2019-08-12 21:04:42

Solution

>数轴上有n 个点,第i 个点的坐标为xi,权值为wi。两个点i,j之间存在一条边当且仅当 abs(xi-xj)>=wi+wj。 你需要求出这张图的最大团的点数。(团就是两两之间有边的顶点集合) 什么鬼题目啊。。。看都看不懂。 别急,我们不妨把问题转换一下: >数轴上有n 个区间,每个区间的范围是[xi-wi,xi+wi]。 求这个数轴上不相交区间(公用端点不算相交)的最大数量。 好眼熟啊。。。。。。等等,这不是线段覆盖问题吗(把区间当成线段处理)? 老套路,我们把每个线段按照右端点排序,再遍历一遍就好了。 代码: ```cpp #include<bits/stdc++.h> //终于懒了一回 using namespace std; struct point{ int l,r; friend inline bool operator < (point x,point y){ return x.r<y.r; } }a[200001]; int n,ans=1; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ int x,w; scanf("%d%d",&x,&w); a[i].l=x-w; a[i].r=x+w; } sort(a+1,a+1+n); for(int i=2;i<=n;i++){ if(a[i].l>=a[i-1].r)ans++; } printf("%d\n",ans); return 0; } ``` 交上去一看,WA!我们分析一下原因。明白了,上面代码的第二个循环的判断出了问题。拿一个例子: ``` ----- ----- ------------ ``` 本来应该选择上面两条线段的,但是结果却是`1`。为什么呢?因为我们在判断的时候是和前一条线段的右端点进行比较的,正确的应该是和已经选择的最右边线段的右端点进行判断。我们开一个变量$R$,它的初值是(排序后)第一条线段的右端点。后面如果选择了一个线段,那么就更新$R$(更新成选择的线段的右端点)。 正解: ```cpp #include<bits/stdc++.h> //终于懒了一回 using namespace std; struct point{ int l,r; friend inline bool operator < (point x,point y){ return x.r<y.r; } }a[200001]; int n,ans=1,r; //这里的r就是描述中的R int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ int x,w; scanf("%d%d",&x,&w); a[i].l=x-w; a[i].r=x+w; } sort(a+1,a+1+n); r=a[1].r; //给R赋初值 for(int i=2;i<=n;i++){ if(a[i].l>=r){ r=a[i].r; //更新R ans++; } } printf("%d\n",ans); return 0; } ``` [$$\textbf{Blog}$$](https://bambusoideae.cn/)