二分
二分的题目由于其方式的特殊性,一般能够二分出来的结果基本上是不会发生超时情况,但是我们需要注意的是二分的对象问题,就是我们应该对什么东西来进行二分。这是我们理解分析二分题目的重中之重。
P1873 砍树
这个题应该算是二分当中比较基础的一道题目,非常简单,我们二分的目标主要就是答案,我们需要砍树的高度就是我们需要去求得的结果,然而这个结果有一个区间,区间显然是0 ——这群树的最高高度。
然后通过二分答案区间组来快速求得答案。
P1024 一元三次方程求解
这道题也应该算是比较经典的一道题目,我们需要通过二分定义答案区间,这个题需要注意的是二分时while循环的条件
P1182 数列分段 Section II
这个题目很有意思,我们二分的是我们所需要的答案,但是我们需要将我们求得答案的区间进行压缩,右区间显然是全部数值相加的和,左区间应该是我们的全部数组中每个元素的最大值.然后通过二分求得该答案是否可行。二分的时候在check函数里面,我们可以定义一个数,来对每一个已经达到我们测试的值进行分装。
P1577 切绳子
本题需要注意的基本点与前面三个结合起来比较像
下面附上ac代码
砍树
伐木工人米尔科需要砍倒M米长的木材。这是一个对米尔科来说很容易的工作,因为他有一个漂亮的新伐木机,可以像野火一样砍倒森林。不过,米尔科只被允许砍倒单行树木。
米尔科的伐木机工作过程如下:米尔科设置一个高度参数H(米),伐木机升起一个巨大的锯片到高度H,并锯掉所有的树比H高的部分(当然,树木不高于H米的部分保持不变)。米尔科就行到树木被锯下的部分。
例如,如果一行树的高度分别为20,15,10和17,米尔科把锯片升到15米的高度,切割后树木剩下的高度将是15,15,10和15,而米尔科将从第1棵树得到5米,从第4棵树得到2米,共得到7米木材。
米尔科非常关注生态保护,所以他不会砍掉过多的木材。这正是他为什么尽可能高地设定伐木机锯片的原因。帮助米尔科找到伐木机锯片的最大的整数高度H,使得他能得到木材至少为M米。换句话说,如果再升高1米,则他将得不到M米木材。
输入格式
第1行:2个整数N和M,N表示树木的数量(1<=N<=1000000),M表示需要的木材总长度(1<=M<=2000000000)
第2行:N个整数表示每棵树的高度,值均不超过1000000000。所有木材长度之和大于M,因此必有解。
输出格式
第1行:1个整数,表示砍树的最高高度。
样例输入:
5 20
4 42 40 26 46
样例输出:
36
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1000010;
int q[N];
int n;
long long m;
bool check(int x)
{
long long sum = 0;
for(int i = n; q[i] > x && i >= 1; i --) sum += q[i] - x;
if(sum >= m) return true;
else return false;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ ) cin >> q[i];
sort(q + 1, q + 1 + n);
int l = 0, r = q[n], mid;
while(l < r)
{
mid = (l + r + 1) / 2;
if(check(mid)) l = mid;
else r = mid - 1;
}
cout << r;
return 0;
}
一元三次方程求解
ax^3 + bx^2 + cx + d;
输入格式
一行,4个实数A,B,C,D
输出格式
一行,3个实根,并精确到小数点后2位。
样例输入:
1 -5 -4 20
样例输出:
-2.00 2.00 5.00
AC代码:
#include<iostream>
#include<stdio.h>
using namespace std;
float a, b, c, d, mid;
float res(float x)//
{
float q;
q = a * x * x * x + b * x * x + c * x + d;
return q;
}
int main()
{
cin >> a >> b >> c >> d;
for(float l = -100; l <= 99; l ++)
{
float r = l + 1;
if(res(l) * res(r) <= 0)
{
while(r - l > 0.00001)
{
mid = (l + r) / 2;
if(res(l) * res(mid) > 0) l = mid;
else r = mid;
}
if(res(mid) < 0.1)
{
printf("%.2f ", mid);
}
}
else continue;
}
return 0;
}
数列分段 Section II
对于给定的一个长度为N的正整数数列A,现要将其分成 M段,并要求每段连续,且每段和的最大值最小。
输入格式
第 1 行包含两个正整数 N,M
第 2 行包含 N 个空格隔开的非负整数 A_i
,含义如题目所述。
输出格式
一个正整数,即每段和最大值最小为多少。
输入样例:
5 3
4 2 4 5 1
输出样例:
6
AC代码:
#include<iostream>
using namespace std;
const int N = 1000100;
int m, n;
int q[N];
bool check(long long x)
{
int s = 0, num = 0;
for(int i = 1; i <= n; i ++ )
{
if(s + q[i] > x )
{
num ++, s = 0;
}
s += q[i];
if(num >= m) return false;
}
return true;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ ) cin >> q[i];
long long sum = 0, max = 0;
for(int i = 1; i <= n; i ++ )
{
sum += q[i];
if(max <= q[i]) max = q[i];
}
long long l = max, r = sum, mid;
while(l < r)
{
mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout << l;
return 0;
}
切绳子
有 N 条绳子,它们的长度分别为 L_i。如果从它们中切割出 K 条长度相同的绳子,这K 条绳子每条最长能有多长?答案保留到小数点后 2 位(直接舍掉 2 位后的小数)。
输入格式
第一行两个整数 N 和 K,接下来 N 行,描述了每条绳子的长度 L_i。
输出格式
切割后每条绳子的最大长度。答案与标准答案误差不超过 0.010.01 或者相对误差不超过 1%1% 即可通过。
输入样例:
4 11
8.02
7.43
4.57
5.39
输出样例:
2.00
AC代码
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N = 10010;
int n, k;
float q[N];
bool check(float x)
{
int num = 0;
float a;
for(int i = 1; i <= n; i ++ )
{
a = q[i];
while(a - x > 0)
{
num ++;
a -= x;
}
if(num >= k) return true;
}
return false;
}
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; i ++ ) cin >> q[i];
sort(q + 1, q + n + 1);
float l = 0.01, r = q[n], mid;
while(r - l > 0.01)
{
mid = (l + r) / 2;
if(check(mid)) l = mid;
else r = mid - 0.01;
}
if(l >= 0.98 && l <= 1.00) l += 0.01;
printf("%.2f", l);
return 0;
}