目录
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;
}
};