题目链接
https://ac.nowcoder.com/acm/contest/5158/B
分析题目
n人队伍,选取若干人,这若干人最大能力值和最小能力值的差值不能大于k,求最多能选取多少人。
解题思路
贪心嘛,自然情不自禁想排序,按能力值从小到大排序。
记得雨巨讲过“尺取法”(应该是)。先想象数轴,再确定一把尺子的长度,拿着确定长度的尺子在数轴上去取数。
这个题一样,排好序之后,遍历数轴以确定长度为k的尺子所对齐的左边界,在尺子右边界以外的点是不符合的,以内是符合的。
大概过程:
step1:升序排序
step2:遍历,尺取,记录尺子长度内的人数
step3:求max,输出
过程实现
我扔了俩代码,一个是我第一遍写的,是方法二对应的;方法一是我第二遍写的。
(为什么写俩?这说来话长……其实就是我开始写的超时了,找不到哪错了,就换了个方式实现,结果还是不行,才发现是数组开小了,忘记+10,回首发现,第一遍写的也对了……)
方法一:
这是比较简单的一串代码,用upper_bound函数找比 尺子左边界+尺子长度 大的第一个人所在的索引,找到了,但是这个人是尺子外的第一个点,所以-1,才是尺子内最右边的点。-a是为了求差,当前地址与首地址的差就是当前位置的索引。
保存最大值,输出。
时间复杂度:O(T*nlogn)
(你要还想减少时间,方法一的代码upper_bound的左边界可以改成a+i,其实对时间复杂度的影响不大,因为log级别的,开个根变化不大)
方法二:
简单的循环,遍历左边界,去找右边界,右边界一定是大于等于上一个左边界对应的右边界的,尺子的特点嘛。所以我们的时间复杂度并非2e5 * 2e5。每次循环完记得把记录当前尺子内人数的cnt自减一下,因为左边界右移了,左边的人出去了,自减之后,再在下一个尺子里继续上次的加就行了。
时间复杂度:O(T*nlogn)(好像是,我记不清了,错了不要打我qwq)
AC代码
#include<bits/stdc++.h> using namespace std; const int N=2e5+10; inline int read(){ int x=0,f=1; char ch; ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); } return x*f; } int a[N]; int T,n,k; int main(){ T=read(); while(T--){ n=read();k=read(); for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+n+1); //——————————方法一 —————————— int ans=0; for(int i=1;i<=n;i++){ int tmp=upper_bound(a+1,a+n+1,a[i]+k)-a-1; // cout<<"when i="<<i<<",tmp="<<tmp<<endl;//测试 ans=max(ans,tmp-i+1); } //——————————————————————— //——————————方法二 —————————— /* int ans=0,cnt=1; for(int i=1,j=2;i<=n;i++){ while(a[j]<=a[i]+k && j<=n) {cnt++;j++;} // cout<<"when i="<<i<<",cnt="<<cnt<<endl;//测试 ans=max(ans,cnt); cnt--; } */ //——————————————————————— cout<<ans<<endl; } }
哔哔赖赖
开始我还把题目当成了要求最大的能力值之和,发现数怎么这么大,和样例答案区别太大了吧,de了一小会才发现求的都不是一个东西。
快读函数,没必要,我就是为了让自己别忘了怎么写,写一遍熟悉一下。