二分猜答案

lc2167

前后缀分解 -> 线性dp

前缀数组记录从左侧处理违禁车厢的最小时间、后缀数组记录从右侧处理的最小时间

遍历每个位置时合并两侧时间,取最小值得到移除所有违禁车厢的最少时间

class Solution {

public:

int minimumTime(string s) {

int n = s.length();

vector<int> suf(n + 1);

for (int i = n - 1; i >= 0; --i)

sufi = si == '0' ? sufi + 1 : ++min(sufi + 1 + 2, n - i); //suf预处理 记录选择++

int ans = suf0, pre = 0;

for (int i = 0; i < n; ++i)

if (si == '1') {

++***pre = min(pre + 2, i + 1);
ans = min(ans, pre + sufi +***++ 1);

}

return ans;

}

};

lc286

前后缀分解

class Solution {

public:

vector<int> productExceptSelf(vector<int>& nums)

{

int n=nums.size();

vector<int> f(n),g(n);

f0=1;

gn-1=1;//init

//空间换时间

//预处理

for(int i=1;i<=n-1;i++)

fi=fi-1*numsi-1;

for(int j=n-2;j>=0;j--)

gj=gj+1*numsj+1;

//cal

vector<int> ret(n);

for(int i=0;i<n;i++)

++reti=fi*gi;++

return ret;

}

};

lc786

二分查找分数值范围,统计小于等于中间值的分数个数,定位第k小的素数分数并返回

#include <vector>

using namespace std;

class Solution {

private:

vector<int> arr;

int n, a, b;

public:

vector<int> kthSmallestPrimeFraction(vector<int>& _arr, int k) {

arr = _arr;

n = arr.size();

double l = 0, r = 1;

while (true) {

double mid = (l + r) / 2;

int cnt = check(mid);

if (cnt > k) r = mid;

else if (cnt < k) l = mid;

else break;

}

return {a, b};

}

private:

int check(double x) {

int ans = 0;

double large = 0;

for (int i = 0, j = 1; j < n; j++) {

while (arri + 1 * 1.0 / arrj <= x)

i++;

if (arri * 1.0 / arrj <= x) {

ans += i + 1;

if (arri * 1.0 / arrj > large) {

a = arri;

b = arrj;

large = arri * 1.0 / arrj;

}

}

}

return ans;

}

};

核心逻辑:用二分查找定位"第k小的素数分数",不用暴力枚举所有分数,效率更高

  1. 前提:输入数组是从小到大排序的素数,要找的是"两个素数相除(分子在前、分母在后,分子<分母)"中第k小的那个分数(比如数组2,3,5,分数有2/3、2/5、3/5,第2小是2/5)

  2. 二分查找的是什么?

不直接找分数,而是找"分数的数值大小"。因为所有可能的分数都在 0~1 之间(分子<分母),所以在 0,1 区间里二分:

  • 每次取中间值 mid ,++统计"所有小于等于 mid 的分数有多少个"(用 check 函数算)++。

  • 如果统计数 >k:说明第k小的分数比 mid 小,缩小右边界;

  • 如果统计数 <k:说明第k小的分数比 mid 大,扩大左边界;

  • 统计数 ==k:说明 mid 刚好"卡"在第k小的分数上,找到目标。

  1. check函数怎么统计?

用"双指针"高效计数(不用两两枚举,避免超时):

  • 固定分母 j ,找最大的分子 i 使得 arri/arrj ≤ mid (因为数组有序, i 越大,分数越大);

  • 此时, i+1 就是以 arrj 为分母、满足条件的分数个数(分子可以是 arr0~arri );

  • 同时记录这些分数中最大的那个(因为统计数==k时,这个最大分数就是第k小的目标)

lc1300

对数组排序后,通过二分查找确定"将数组中大于该值的元素替换为该值后总和最接近目标值"的数,用二分+迭代器定位_计算总和

using namespace std;

class Solution

{

public:

int findBestValue(vector<int>& arr, int target)

{

sort(arr.begin(),arr.end());

int l=0, r=arr.back();

int n=arr.size();

auto check=\&(int x)->int

{

int ret=0;

auto it=lower_bound(arr.begin(),arr.end(),x);

int idx=it-arr.begin();

for(int i=0;i<idx;i++)

ret+=arri;

return ret+(n-idx)*x;

};

while(l<=r)

{

int m=l+(r-l)/2;

if(check(m)<target)

l=m+1;

else

r=m-1;

}

int sum1=check(l), sum2=check(l-1);

return abs(sum1-target) < abs(sum2-target) ? l : l-1;

}

};

相关推荐
Logic10119 分钟前
C语言/数据结构位运算题解:异或XOR找出套碗游戏中的“幸运碗“——只出现一次的数字
c语言·数据结构·算法·数组·位运算·时间复杂度·空间复杂度
yi01138 分钟前
LeetCode 3. 无重复字符的最长子串|不定长滑动窗口:什么时候更新最大值?
笔记·python·算法·leetcode·双指针·滑动窗口
y1su1 小时前
Leetcode 单调栈模板
java·开发语言·数据结构·算法·leetcode
索尔~古德曼1 小时前
CF(1400-1400&1300-1500)
算法
爱刷碗的苏泓舒1 小时前
泰勒级数与泰勒展开:GNSS卫地距线性化、Jacobian矩阵与Hessian矩阵
线性代数·算法·矩阵·gnss·雅可比矩阵·卫地距线性化·海森矩阵
垆边人似月.1 小时前
华为题:每日温度
数据结构·c++·算法
Sylvia-girl1 小时前
堆排序算法
算法·排序算法·c
Peregrine92 小时前
数据结构 - > 树和二叉树
数据结构·算法
我是人✓2 小时前
Java面向对象核心特性深度解析
java·前端·算法
j7~2 小时前
【算法】专题四.前缀和:一维二维模板,配套 LeetCode 真题,结合同余定理、哈希前缀和,搞定子数组与子矩阵区间求和问题。
数据结构·c++·算法·前缀和·优选算法