终于到学校进行模拟了!
(结果下午又通知有台风导致26号下午和27号停课了)
(最后27号台风绕道了走一滴雨都没有看到)
模拟的T1是clique(洛谷传送门)
这是一道cf原题
我一开始写的是暴力的做法结果最后提交的时候所有的点全部都WA了直接爆零
后来下午讲题的时候lxy同学讲的这道题发现这道题居然是有(n)的线性做法的!
(lxy yyds!)
先粘贴一下原题大意:
题目描述 数轴上有 n 个点,第 i 个点的坐标为 xi,权值为 wi。两个点 i,j 之间存在一条边当且仅当 abs(xi−xj)≥wi+wj。 你需要求出这张图的最大团的点数。 团的定义:两两之间有边的顶点集合。
输入格式 第一行一个整数 n,接下来 n 行每行两个整数 xi,wi 。
输出格式 一行一个整数表示答案。
这道题是一道贪心问题
首先来分析一下题目:
我们有n个点,对于每个点有一个坐标和一个权值。只有当|xi-xj|>=wi+wj时二者之间才能够相连
我一开始的想法是进行n²的遍历,直接万能O(n²)。虽然样例点过了,但是出了一些莫名其妙的锅,评测的时候所有点直接全部wa掉爆零。
所以也就不详细说O(n²)的做法了
直接讲一下下午讲题所讲的O(n)做法
1)先讲一下纯数学推导方法
首先,我们知道了一个基本事实:
1.只有当|xi-xj|>=wi+wj时二点之间才能够相连
1°那么不妨设xj>xi
∴则有xj-xi>=wi+wj
∴xj-wj>=xi+wi
所以对于所有点的坐标在右边的点,我们只需要使得其x-w大于其左面的点的x+w即可了
2°设xi<xj
∴则有xi-xj>=wi+wj
∴xj+wj<=xi-wi
在这种情况下所有点的坐标在右边的点,我们需要使得其x+w小于其左边的点的x-w即可
(这两种情况均可构造代码,最大的区别是cmp的不同)
然后我们会想到讲一个点的所有元素均放置到一个结构体内,然后按照其中的一个元素进行排序即可
然后我们就需要来考虑如何排序了
Ⅰ.首先考虑能否用x排序呢?
答案是否定的。
为什么?
(因为我亲自用x排序之后测了一遍发现不行)
因为用x排序的话是不满足贪心的策略的,也就是说,用x进行排序的话所求的答案并不是我们想要的最大值。
比如说这组数据:
4 0 1 10 8 13 1 16 1
我们采用1°的策略进行运算,我们最终会发现所得的值为2。
但是我们只要稍加计算便可以发现实际上最大值为3。
为什么会有这样的结果?
原因便是我们使用了x进行排序并没有符合贪心算法的规定。
所以看到这组数据我们便想到可以用x+w进行排序!
Ⅱ.用x+w进行排序
由于我们最后在1°中最终推导出来的式子为xi-wi>=xj+wj
所以便应当对x+w进行升序排序然后用每一个后面的x-w来与前面的x+w进行比较,如果符合大于等于的关系的话就说明答案可以更新
此处规定i,j为按照x+w排序后的先后顺序排列,i在前,j在后
那么此时就会有一个问题了?因为此时i,j是通过x+w进行排序的,那么不一定有xi<xj。如果xi>xj的话不就违背了1°的前提条件了吗?如此说来1°不就应该不成立了吗?
所以在此对这个问题进行一下证明(此处也是我之前一直无法理解的难点):
那么在这个已经按照x+w为cmp排序标准的数组中,我们已知在这个数组中的下表中 i<j 。 那么我们就已知xi+wi<=xj+wj 如果xi<xj,则上述更新的规则明显适用。 如果xi>xj,则1°不成立,则上述所描述的规则的前提条件不成立 所以在此时2°成立,我们应当判断是否有后面的x+w小于等于前面的x-w 即判断是否有xj+wj<=xi-wi 但是由于我们已知xi+wi<=xj+wj,以及w>=1 所以通过不等式相关知识,我们可以得到: xi-wi<xi+wi 又通过xi+wi<=xj+wj 所以易得xi-wi<xj+wj 此既定事实与所希望求得的xj+wj<=xi-wi矛盾 所以一定不存在通过x+w排序的数组中存在xi>xj且能够更新答案的情况 QED.
所以我们可以得到在这种条件下只需要使用1°的判断策略就可以了(因为2°的情况无影响)
把这种做法的AC标程贴在此处:
1 #include<bits/stdc++.h> 2 #define N 210000 3 using namespace std; 4 int n,ans=1,t=1; 5 struct node{ 6 int x,w,l,r; 7 }a[N]; 8 bool cmp(node q,node w){ 9 return q.r<w.r; 10 } 11 int main(){ 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++){ 14 scanf("%d%d",&a[i].x,&a[i].w); 15 a[i].l=a[i].x-a[i].w; 16 a[i].r=a[i].x+a[i].w; 17 } 18 sort(a+1,a+1+n,cmp); 19 for(int i=2;i<=n;i++){ 20 if(a[i].l>=a[t].r){ 21 ans++; 22 t=i; 23 } 24 } 25 printf("%d",ans); 26 return 0; 27 }
Ⅲ.用x-w进行排序
当时讲题的时候大家都是用x+w进行排序的,从而默认讲2°的判断策列直接排除掉了
那么我在理解了这道题后就在思考2°的策略能够进行相关的应用呢?
答案是肯定的。
其实对x-w进行排序的主要思路与Ⅱ做法相差不大,只不过排序需要进行降序排序,并且要运用2°的判断策略罢了
把这种做法的AC标程贴在此处:
1 #include<bits/stdc++.h> 2 #define N 210000 3 using namespace std; 4 int n,ans=1,t=1; 5 struct node{ 6 int x,w,l,r; 7 }a[N]; 8 bool cmp(node q,node w){ 9 return q.l>w.l; 10 } 11 int main(){ 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++){ 14 scanf("%d%d",&a[i].x,&a[i].w); 15 a[i].l=a[i].x-a[i].w; 16 a[i].r=a[i].x+a[i].w; 17 } 18 sort(a+1,a+1+n,cmp); 19 for(int i=2;i<=n;i++){ 20 if(a[i].r<=a[t].l){ 21 ans++; 22 t=i; 23 } 24 } 25 printf("%d",ans); 26 return 0; 27 }
Ⅳ.用w进行排序
说实话用w进行排序的错误性我还真的没办法进行说明(主要是因为一看就不可能)
如果要进行阐述的话那么就是因为w和x是平级的关系,既然用x进行排序都是不可以的,那么用w进行排序必然也是不可以的(感性理解)
2)讲一下非纯数学推导方法(数形结合)
可能看到此处有人就会惊讶了:这道题又不是平面直角坐标系,他只是一个数轴怎么可能进行数形结合呢?
答案是:虽然这只是一个数轴,但仍然是可以进行数形结合解释的。
因为我们已经知道了w>=1所以我们可以将x-w到x+w理解成一个区间[ x-w , x+w ]
而两点之间可以连接一条边的条件便是两个区间之间没有交点或者交集只有一个点
是不是突然很简单明了了!
其他的思路什么的就可以结合此处的数形结合思想从而更好地理解上述纯数学推导方法中的内容了。
总结:贪心问题一般需要通过某个元素来对齐进行排序之后再进行相关操作
THE END.