two-pointer 双指针法—初步介绍

例题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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值