二分

二分

代码 - 自带函数

int k = upper_bound(a, a + n , x) - a;//返回大于x的数的下标 
int k = lower_bound(a, a + n , x) - a;//大于等于x的数的下标

代码 - 自定义函数

//使用二分法,要保证数组已经升序排列

int bin1(int x){
	int l = 0, r = n - 1, mid;
	while(l < r)
	{
		mid = (l + r) / 2;
		if(a[mid] >= x) r = mid;
		else l = mid + 1;
	}
	return r; //绝对不会越界,如果有这个值一定是指向第一次出现的时候
	//没有相同的值则返回比x大的数的下标
}



int bin2(int x){
	int l = 0, r = n-1, mid;
	while(l < r){
		mid = (l + r + 1) >> 1; //避免死循环,需 + 1
		if(a[mid] <= x) l = mid;
		else r = mid - 1;
	}
	return l; //如果有这个值一定是指向最后一次出现
	//没有相同的值则返回比x小的数的下标
}

//不建议使用
int BinSearch(int x) {
	int left = 0, right = n - 1, mid;
	while(left <= right) {
		mid = (left + right) / 2;
		if(a[mid] == x)return mid;
		else if(a[mid] > x)
			right = mid - 1;
		else
			left = mid + 1;
	}
	if(right < 0)return 0;
	return right;	//如果返回  r 或者 l 有越界的风险
	//直接用于判断有无这个数,这种二分是可取的,特殊情况特别对待 
}


二分答案

题目1

蒜头君的生日要到了!根据习俗,他需要将一些派分给大家。

他有 N个不同口味、不同大小的派。有 F 个朋友会来参加派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。

朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,也要给自己留一块,而这一块也要和其他人的同样大小。

请问每个人拿到的派最大是多少?每个派都是一个高为 1,半径不等的圆柱体。

输入格式

第一行包含两个正整数 N 和 F,1 ≤N,F≤ 10000,表示派的数量和朋友的数量。

第二行包含 N 个 1 到 10000之间的整数,表示每个派的半径。

输出格式

输出每个人能得到的最大的派的体积,精确到小数点后三位。

代码

#include<bits/stdc++.h>
using namespace std;

double a[10005];
double n, f, l, r = -1, mid;
const double PI=acos(-1.0);
const double E = 1e-5;

double check(double mid) {
	int sum = 0;
	for(auto x: a) {
		while(x - mid > 0)sum ++, x -= mid; 
//              注意while的条件不能错写成 x > 0 或者 x - mid
        if(sum >= f + 1)return true;
	}
	return false;
}

void bin() {
	while(r - l > E) {
		mid = (r + l) / 2;
		if(check(mid)) {
			l = mid;
		} else r = mid;
	}
	printf("%.3lf", r); //一般答案的精度要求就是比题目要求的输出位数多2,如此题E为1e-5
}

int main() {
	cin >> n >> f;
//      二分答案题一般有实际应用场景,读题需仔细,比如这题要注意到分的是派的体积
	for(int i = 0 ; i < n; i++) {
		cin >> a[i], a[i] = a[i] * a[i] * PI;
		if(a[i] > r)r = a[i];// r = max(r, a[i]);
	}
	bin();
	return 0;
}

题目2

给定一个浮点数,求它的三次方根, 结果保留6位小数, 数据范围
−10000≤n≤10000

代码

#include<bits/stdc++.h>
using namespace std;

const double E = 1e-8;
double n;

bool check(double mid) {
	return pow(mid, 3) >= n;
}

void bin() {
	double l = -100, r = 100, mid;
	while(r - l > E) {
		mid = (l+r)/2;
		if(check(mid)) r = mid; //此题的r与l与上题是相反的,注意所求所需的答案是哪一部分
		else l = mid;
	}
	printf("%.6lf", l);
}

int main() {
	cin >> n;
	bin();
	return 0;
}

题目3

有n个工人和m个任务。
每个任务都应该有一个工人分配给它。如果一个工人精通该任务,他们在1小时内完成。否则,他们需要2小时。
工人们并行工作,彼此独立。每个工人一次只能做一个任务。
所有任务能在多长时间内完成?

输入
第一行包含一个整数t(1≤t≤104)--测试用例的数量。
每个测试案例的第一行包含两个整数n和m(1≤n≤m≤2·10^5)--工人数和任务数。
第二行包含m个整数a1,a2,...,am(1≤ai≤n)--精通第i项任务的工人的索引。
所有测试案例的m之和不超过2·10^5。

输出
对于每个测试案例,打印一个整数--所有任务能够完成的最小时间。

Example

input

4
2 4
1 2 1 2
2 4
1 1 1 1
5 5
5 1 3 2 4
1 1
1

output

2
3
1
1

代码

//暴力枚举
//最少时间为1, 最多时间为2*m —— 即只有一个人并且他不擅长所有的工作
//故遍历时间的复杂度最多也为2m,即4*10^5
//基本思路:假定时间,看这个时间下能完成多少任务数,如果大于所规定的任务数即是合法的
#include<bits/stdc++.h>
using namespace std;

const int N = 200005;
int a[N];

int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t;cin >> t;
    while(t --){
        int n, m; cin >> n >> m;
        for(int i = 1; i <= n; i++)a[i] = 0;//多组读入记得初始化
        for (int i = 0; i < m; ++i) {
            int x; cin >> x;
            a[x] ++;
        }
        int ans, k;
        for(k = 1;  k <= 2*m; k++){
            ans = 0;
            for(int i = 1; i <= n; i++){
                ans += min(a[i], k);
                if(k > a[i]) ans += k - a[i] >> 1;
            }
            if(ans >= m)break;
        }
        cout << k << '\n';
    }
    return 0;
}

//二分答案
#include <bits/stdc++.h>
using namespace std;

int a[200005];

int main(){
    ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
    int t; cin >> t;
    while(t --){
        int n, m; cin >> n >> m;
        for(int i = 1; i <= n; i++)a[i] = 0;
        for (int i = 0; i < m; ++i) {
            int x; cin >> x;
            a[x]++;
        }
        long long k = 0;

        long long l = 1, r = 2 * m;
        while(l < r){
            int mid = l + r >> 1;
            k = 0;
            for (int j = 1; j <= n; ++j) {
                k += min(mid, a[j]);
                if(a[j] < mid) k += mid - a[j] >> 1;
            }
            if(k >= m) r = mid;
            else l = mid + 1;
        }
        cout << l << '\n';
    }
    return 0;
}
posted @ 2022-07-05 14:57  每日一题,医生远离  阅读(78)  评论(0)    收藏  举报