例题1:给出n个数,找出和最接近k的两个数。
思路 :对数组进行排序,一个指针指向1(最小),另一个指向n(最大)。如果两指针之和大于k,则右指针左移1;如果两指针之和小于k,则左指针右移1。
其中左移意为使值变大,右移意为使值变小,两两交换移动,就能最大可能的接近k。
例题2:给出n个数,找出和最接近k的三个数。
思路 :枚举1个数,其余与例题1一致。注意对于nums[st],nums[ed]和nums[i],都要判断是不是与前一个数相等。
例题3:给出n个数,问最多能组成多少个三角形。
思路 :枚举b(第2边),一个指针指向a,另一个指向c,如果满足a+b>c就是一个三角形,同时得知 b后面的数+c>a一定成立,然后end--。如果b+c<a,就start++。
例题4:给你一个长度为n的序列,对于所有长度>=k的选出其中的第k小元素,于是得到一个新的集合,求出这个集合中的第l大元素,数据范围n<=100000,所有数<=1e8。
思路 :考虑二分答案,对于当前答案x,如果至少包含k个小于等于x的区间的数量<l,那么说明答案太大了,反之答案太小了。我们只需要考虑如何计算区间数就可以了。
对于当前答案x, 我们对于原数列的每个数如果>x 那么赋值为0,否则为1。于是我们只需要计算和大于等于k的区间数就可以了,这个显然是可以用two-pointer在O(n)时间内完成的。代码:
#include <bits/stdc++.h>
#define Maxn 2000007
using namespace std;
int n,k;
long long l;
int a[Maxn],b[Maxn];
long long tryit(int x)//计算和大于等于k的区间数
{
for (int i=1;i<=n;i++)
if (a[i]>x) b[i]=0;
else b[i]=1;
long long ans=0;
int now=1,r=b[1];//now是右指针,r是累计大于x的数的个数
for (int i=1;i<=n;i++)
{
while (now<=n&&r<k) r+=b[++now];
if (now==n+1) break;
ans+=n-now+1;//以n为右端点的区间(必定满足“和大于等于k的区间数”)共有n-now+1个
r-=b[i];//右端点移出了i
}
return ans;
}
int main()
{
scanf("%d%d%lld",&n,&k,&l);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
int lx=1,rx=100000000;
while (rx-lx>1)
{
int mid=(lx+rx)/2;
if (tryit(mid)>=1LL*(n-k+1)*(n-k+2)/2-l) rx=mid; else lx=mid;
}
if (tryit(rx)>=1LL*(n-k+1)*(n-k+2)/2-l) printf("%d\n",rx); else printf("%d\n",lx);
return 0;
}