二分题目练习
wscg,重新学习二分答案。。
P1843 奶牛晒衣服
\(mid\)是我们花费的时间,对于每件衣服判断再\(mid\)时间内能不能被自然烘干,如果不能就需要烘干机,计算需要烘干机的时间(烘干机是独立的),如果时间在\(mid\)以内说明是\(mid\)是合法的,否则\(mid\)就不合法。
int q[N];
bool chek(int mid) {
int s = 0;
for (int i = 1; i <= n; i++) {
if (q[i] - mid * a > 0) {
s += ceil((q[i] - mid * a) / b);
}
}
if (s <= mid) return 1;
else return 0;
}
void solve() {
cin >> n >> a >> b;
for (int i = 1; i <= n; i++) {
cin >> q[i];
}
int l = 0, r = 1e9;
while (l <= r) {
//因为是要找最小值,所以mid合法时更新的是r,不合法时更新l。
int mid = (l + r) / 2;
if (chek(mid))
r = mid - 1;
else
l = mid + 1;
}
cout << l << '\n';
}
P1577 切绳子
比较简单的浮点数二分,不用担心边界问题。
double a[N];
bool check(double mid) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (a[i] >= mid) {
cnt += a[i] / mid;
}
}
if (cnt < m)
return 0;
else
return 1;
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
double l = 0.0, r = 100000000.0;
while (r - l > eps) {
double mid = (l + r) / 2.0;
if (check(mid)) {
l = mid;
} else {
r = mid;
}
// printf("%.8lf %.8lf\n", l, r);
}
printf("%.1lf\n", l);
}
P1182 数列分段 Section II
数列分段,关键点在于判断分的段数是否合法,如果最少需要段数比我们需要的段数要小,那我们可以通过在每一段的内部分段使得段数达到\(K\)但是如果要多的话显然是不合法的。
其次,由于二分时的区间取值问题,很明显,对于\(L\)的赋值应该取数列中的最大值,而r应该取数列的总和。(如果$L4赋值为0或1,第4个点会wa)。
bool chek(int mid) {
int sum = 0, k = 0;
for (int i = 1; i <= n; i++) {
if (sum + a[i] <= mid)
sum += a[i];
else {
sum = a[i];
k++;
}
}
if (k >= m) return 0;
else return 1;
}
void solve() {
cin >> n >> m;
int l = 0, r = 1e9;
for (int i = 1; i <= n; i++) {
cin >> a[i];
l = max(l, a[i]);
}
while (l <= r) {
int mid = (l + r) / 2;
if (chek(mid))
r = mid - 1;
else
l = mid + 1;
}
cout << l << '\n';
}
/*
4 2 4 5 1
*/
P3743 小鸟的设备
这个题比较的毒,\(check\)函数十分好想,但是\(r\)数据范围必须开到1e10,但是由于我们处理数据肯定是希望精度越高越好,但是如果\(eps\)开到了1e-8,那么代码就会\(TLE\),这就是为什么一直过不了第19个点的原因,因此\(eps\)只要设为1e-5就可以了。
double b[N], a[N];
double p;
bool chek(double mid) {
double sum = 0;
for (int i = 1; i <= n; i++) {
double k = b[i] / a[i];
if (k < mid) {
sum += (mid * a[i] - b[i]) / p;
}
}
if (sum > mid) return 0;
else return 1;
}
void solve() {
cin >> n >> p;
double s = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
s += a[i];
}
if (p >= s) {
cout << "-1.000000000" << '\n';
return;
}
double l = 0.0, r = 1e10;
while (r - l > eps) {
double mid = (l + r) / 2.0;
if (chek(mid))
l = mid;
else
r = mid;
}
printf("%.8lf\n", l);
}
/*
*/
P3853 [TJOI2007] 路标设置
这和之前选石头的题目思路基本一致,主要注意一下边界问题即可。
bool chek(int mid) {
int y = k;
int la = 0;
for (int i = 1; i <= n; i++) {
if (y < 0) break;
if (a[i] - la <= mid) {
la = a[i];
} else {
la = la + mid;
i--;
y--;
}
}
if (y >= 0)
return 1;
else
return 0;
}
void solve() {
cin >> L >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int l = 0, r = L;
while (l <= r) {
int mid = (l + r) / 2;
if (chek(mid)) {
r = mid - 1;
} else
l = mid + 1;
}
cout << l << '\n';
}