6.12 二分练习

 

目录

CF1692E Binary Deque 二分枚举端点判断

532. 数组中的 k-diff 数对 - 力扣(LeetCode) 排序+二分

719. 找出第 K 小的数对距离 - 力扣(LeetCode)排序+二分答案


CF1692E Binary Deque 二分枚举端点判断

题意:

给定由01组成的数组,每次可以删除头部或者尾部元素。问使剩余数组的和为k的最小操作数。

思路:

题意很简单,就是找到等于k的前缀和,但是要最小操作,也就是如果有0就不能删了,这样才会满足结果且操作最少。所以去找前缀和为k的最长子段。因为前缀和数组是有序的,所以可以枚举一个右端点,二分查找使得子段和为k的左端点,然后还要使子段最长。二分左指针还要尽量左偏。

也就是找到合法解之后还要让左指针左偏继续找合法解。

#include<bits/stdc++.h>
#pragma warning (disable:4996);
#define ll long long
#define	int ll
#define mm(a) memset(a,0,sizeof(a))
using namespace std;
const int N = 2e5 + 10;
int T, n, m, k, q;
int a, b, c, d;
int sum[N];
int myfind(int l, int r, int k, int s) {//s为右端点
	int mid = (l + r) >> 1;
	while (l + 1 != r) {
		mid = (l + r) >> 1;
		if (sum[mid] - sum[s - 1] <= k) {
			l = mid;//尽量左偏
		}
		else
			r = mid;
	}
	return l;
}
signed main() {
	ios::sync_with_stdio(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("out.txt", "w", stdout);
#endif	
	cin >> T;
	while (T--) {
		cin >> n >> k;
		int ok = 1;
		int mx = INT_MIN;
		for (int j = 1; j <= n; j++) {
			cin >> a;
			sum[j] = sum[j - 1] + a;
		}
		for (int j = 1; j <= n; j++) {
			int ans = 0;
			int t = myfind(j - 1, n + 1, k, j);//枚举右端点
			if (sum[t] - sum[j - 1] == k) {//需要判断二分的答案是否合法,也就是找到了没有
				ok = 0;
				mx = max(mx, t - j + 1);
			}
		}
		if (ok)
            cout << -1 << endl;
		else 
			cout << n - mx << endl;
	}
}

532. 数组中的 k-diff 数对 - 力扣(LeetCode) 排序+二分

 因为是讨论绝对值,所以i<j的限制条件无效。所以先对原序列排序,使 nums[i] > nums[j] 成立。枚举右端点,然后去找值为 nums[i] - k的nums[j]的左端点。这题只需要找到结果没有其他限制,所以找到后判断结果是否合法即可。

这题还要注意的是需要去重,因为是要不同的数对,因为是枚举右端点,所以应该从重复元素中拿出最后一个。(这样也会使k=0时,(1,1)合法)

class Solution {
public:
    int findPairs(vector<int>& nums, int k) {
            int n=nums.size();
            sort(nums.begin(),nums.end());
            int ans=0;
            for( int j=0;j<n;j++){
                if(j!=n-1&&nums[j]==nums[j+1]){//去重
                    continue;
                }
                int x=nums[j];
                int t=myfind(nums,-1,j,nums[j]-k);
                if(t<j&&t>=0&&nums[j]-k==nums[t]){//判断结果是否合法
                    ans++;
                }
            }
            return ans;
    }
    int myfind(vector<int>&nums,int l,int r,int k){
        int mid;
        while(l+1!=r){
            mid=(l+r)>>1;
            if(k>=nums[mid]){
                l=mid;
            }else{
                r=mid;
            }
        }
        return l;
    }
};

719. 找出第 K 小的数对距离 - 力扣(LeetCode)排序+二分答案

 同上一题。先排序,然后二分枚举答案,在每次枚举的答案再判断一次是否合法,当前答案是所有数对距离的第几。从而条件二分答案的范围。

二分答案的过程很简单,重点在于如何判断这个答案合法。我们可以枚举右端点,分析以每个元素为右端点,期间距离对小于二分答案mid 的个数。然后统计完整个序列中数对距离小于答案的个数 cnt。

如果cnt>mid说明枚举的答案太大了,导致更多比这个答案小的数对。右指针左移使答案变小。

反之同理。

而这个统计小于mid的数对的操作可以通过二分实现——在左区间找到第一个大于等于nums[j]-mid的元素,枚举的右端点和这个元素之间的所有元素与右端点组成的数对距离都会小于mid。

class Solution {
public:
	int smallestDistancePair(vector<int>& nums, int k) {
		int n = nums.size();
		sort(nums.begin(), nums.end());
		int l = -1, r = nums.back() - nums.front() + 1;
		while (l + 1 != r) {
			int cnt = 0;
			int mid = (l + r) >> 1;
			for (int j = 0; j < n; j++) {
				int t = myfind(-1,j,nums[j]-mid,nums);
				cnt += j - t;
			}
			if (cnt >= k) {
				r = mid;
			}
			else  {
				l = mid;
			}
		}
		return r;
	}
     int myfind(int l,int r,int k,vector<int>&nums){
        while(l+1!=r){
            int mid=(l+r)>>1;
            if(nums[mid]>=k)
                r=mid;
            else
                l=mid;
        }
        return r;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值