二分
二分
代码 - 自带函数
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;
}

浙公网安备 33010602011771号