蓝桥杯算法入门_16 (二分法-进阶)

二分法与快速幂

二分法 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;
}

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值