二分法与快速幂
二分法 O(logn) – 使用前提 有序序列!
例:猜 1 - 100 中的一个数
输入:
5
2 5 6 9 10
4
9 6 4 10
输出: (查找元素 对应的下标位置 , -1表示未找到)
3
2
-1
4
#include <iostream>
using namespace std;
const int N_01 = 1e4 + 9;
int a_01[N_01] , n_01 , m_01;
int binary_search_01( int x) {
int l = 0,r = n_01 - 1; //边界
while(l <= r) {
int mid = (l + r) >> 1 ;
if(a_01[mid] == x) {
return mid;
}
if(a_01[mid] < x) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return -1; //没找到
}
void test_01() {
cin >> n_01; //数组长度
for(int i = 0; i < n_01; i++) {
cin >> a_01[i]; //录入数据
}
cin >> m_01; //查找次数
while(m_01--) {
int x;
cin >> x; //查找值
cout << binary_search_01(x) << endl; //找到返回下标 ,-1表示未找到
}
return;
}
//略例 方程精度解
/*快速幂
a = a*a;
x的y次方 % mod == { y奇数 x^(y/2)^2 * x % mod
{ y偶数 x^(y/2)^2 % mod
如 x^5 == (x^2)^2 * x ;
输入 x y p
2 10 10007
输出:
1024
1024
*/
int pw1(int x,int y,int p) { //写法1 (记这个,理解了)
if(!y) { //y == 0时
return 1;
}
int res =pw1(x,y/2,p); // x^(y/2) % p
res = res * res % p;
if(y & 1) { // 若为奇数次 x^(y/2) * x^(y/2) * x % p;
res = res * x % p;
}
return res;
}
int pw2(int x,int y,int p) { //写法2
int res = 1;
while(y) { //y != 0时
if(y & 1) { //y为奇数时 (或遍历到最后时 y == 1)
res = res * x % p; // 若y为偶数 则最后变为1 ,res == x ,结束循环 // 也包括了特殊情况 y= 0,return res = 1;
}
y >>= 1; // y = y / 2 ;
x = x * x % p;
}
return res;
}
void test_02() {
int x,y,p;
cin >> x >> y >>p;
cout << pw1(x,y,p) << endl;
cout << pw2(x,y,p) << endl;
return;
}
二分法求分组解
一组数 , 分成k组 ,使每组的和的最大值 最小
转换思路:(分成k组 ,每组≤sum)
若分一组 k == 1 ,则最小值 即为所有元素的最大值
二分答案 求解
输入:
8 4
1 3 4 7 1 4 3 8
输出:
8
const int N_03 = 1e3 + 9;
const int inf_03 = 0x3f3f3f3f;
int n_03,k_03,a_03[N_03];
bool check(int x) { //判mid为最大值分组是否可以--贪心思想 ,因为是连续区间 所有从第一个组开始就往最多的里面分,看最后分的组是否小于k_03
int now = 0,cnt = 0;
for(int i = 0; i < n_03; i++) {
if(now + a_03[i] > x) { //now为当前每组的值的和
cnt++; //统计当前组数
now = a_03[i]; //大于mid , 就新开一组
} else {
now += a_03[i];
}
}
return cnt <= k_03; //分组是否存在 ,(小于k_03组说明能分的更小,可行)
}
int cal_03(int l,int r) { //二分计算
while(l < r) {
int mid = (l + r) >> 1; // >> 1 等效 除2 ,--不要写错!!!!!
if(check(mid)) {
r = mid; //符合分组 ,mid最大值减小,判断是否有更小的
} else {
l = mid + 1; //不符合 ,只能往更大的mid去找
}
}
return l; //答案 l == r 最终结束状态
}
void test_03() {
int ma = -inf_03,sum = 0;//最小值ma
cin >> n_03 >> k_03; // n_03个数 分k_03组
for(int i = 0 ; i < n_03; i++) {
cin >> a_03[i];
ma = max(ma ,a_03[i]); //ma 初始为a_03[i]最大值 (真实情况一定比这个值大) 为左边l
sum += a_03[i]; //所有数的总和 (最右边r) 任何一个组的和 不会超过sum , 极限情况 k_03 == n_03 ,sum == ∑a_03[i]
}
cout << cal_03(ma,sum) << endl; //二分计算
return;
}
01分数规划问题
给出n个物品
每个物品收益a[i] ,代价b[i] ,使选择的k个元素 的 ∑a[i] / ∑b[i] 最大值
∑a[i] / ∑b[i] == v
a - v*b < 0 v过大 反之v过小
最优比例生成树! (难 暂时略)
题目:; 给定n个二元组 (a,b) ,选择k个二元组 使得选出的元素a的和 与 元素b的和 的比值最小
输入:
3 1
5 0 2
5 1 6
输出:
0.83
#include<algorithm>
const int N = 1e3 + 9;
const double eps = 1e-7; //10的负7次方
int n,k;
double a[N],b[N],tmp[N];
double g(double v){
for(int i = 0;i < n;i++){
tmp[i] = a[i] - v*b[i]; //已经知道v,测试 算出每个二元组a[i] - v*b[i] 的值
}
sort(tmp,tmp + n);
double sum = 0; //选出最大的n-k个数 进行求和
for(int i = k;i < n;i++){
sum += tmp[i];
}
return sum;
}
double cal(){
double l = 0,r = 1e10;
while(r - l > eps){
double mid = (l + r) / 2;
if(g(mid) > 0){
l = mid;
}else{
r = mid;
}
}
return l;
}
void test_04(){
cin >> n >> k;
for(int i = 0;i < n;i++){
cin >> a[i];
}
for(int i = 0;i < n;i++){
cin >> b[i];
}
printf("%.2f\n",cal());
return;
}
练习题带更新…
int main() {
test_04();
return 0;
}