二分三分以及01分数规划
二分算法
二分算法的本质是求严格增或严格减函数,数列,数组中的某一特值,通过将整个区间无限对半开,找到答案所在的区间,经过多次迭代后区间左边界等于区间右边界,或两者无限逼近,那么这个答案就是解。
既然知道二分就是对于n个数对半开,那么如果将查询每一个数的时间复杂度视作为单位时间复杂度o(1),二分的时间复杂度就为o(log n),这比在数组中遍历每个元素o(n),快上很多。
在一般算法竞赛中或者实际生活中,有时会需要我们去用到二分算法,那么他的标准模板是什么呢?我们如何在c++中运用它呢?
1 2 3 4 5 6 7 8 9 10
| int binarySearch(vector<int>& a, int target) { int l = 0, r = a.size() - 1; while (l <= r) { int mid = (l + r) / 2; if (a[mid] == target) return mid; else if (a[mid] < target) l = mid + 1; else r = mid - 1; } return -1; }
|
首先在二分函数中确定左边界与右边界,通过一个while循环来实现递归的目的,在while中建立一个mid变量,来让他作为新的左右边界来求出答案。
手写二分算法往往是很麻烦的事情,不过在c++中,为了求某些特殊情况,c++会给你一些封装的stl来实现。比如你要求数组中最小的元素或者最大的元素,你可以通过lower_bound()或upper_bound()方法来查询,这并不要求数组有序,因为排序的操作是在你使用时候自动进行了,它的时间复杂度也是o(log n)。
三分算法
三分算法顾名思义就是把一个数组或区间分为三份,通常用于求凹凸函数的最大值最小值。本质类似与二分,先给出左右端点,再求出两个三等分点,将两个三等分点的函数值进行比较,判断答案处于哪个区间内,然后把其中一个三等分点作为新的端点,进行迭代,求出答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int ternarySearch(int l, int r) { while (r - l >= 3) { int mid1 = l + (r - l) / 3; int mid2 = r - (r - l) / 3; if (f(mid1) < f(mid2)) r = mid2 - 1; else l = mid1 + 1; }
int res = l; for (int i = l + 1; i <= r; ++i) if (f(i) < f(res)) res = i; return res; }
|
01分数规划
题面详见 https://ac.nowcoder.com/acm/contest/22353/1011
这道题是01背包问题的变种,可以用二分去写,思路就是先用结构体定义每个物品,将所求 ∑w/∑v=r,r要max转化为 ∑w-r*∑v=0,找到最大的r。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <bits/stdc++.h> using namespace std; int n,k; struct item{ double w,v; }; vector <item> items; bool check(double R){ vector <double> temps; for(int i=0;i<items.size();++i){ temps.push_back(items[i].v-R*items[i].w); } sort(temps.rbegin(),temps.rend()); double sum=0; for(int i=0;i<k;++i){ sum+=temps[i]; } return sum>=0; } int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n>>k; items.resize(n); for(int i=0;i<n;++i){ cin>>items[i].w>>items[i].v; } double left=0;double right=1000001; for(double i=0;i<100;++i){ double mid=left+(right-left)/2; if(check(mid)){ left=mid; }else{ right=mid; } } cout<<fixed<<setprecision(10)<<left<<endl; return 0; }
|