这周的课程是二分,一听到二分,我就会想起二分查找,其实二分的思想在许多的数据结构中都有广泛的用处,那今天的二分呢,实际上实际上是一种解题的策略:
“二分答案”,也就是说,如果一道题目的答案落在某一个区间中,如果某个答案在验证正确性以后,可以确定该答案以后或以前的数值都不是最优时,就可以使用该方法了。
POJ 1064 N条线,从中切K条长度相等的线,求最长可多长?
答案可定在(0, maxlen]之中, 如果其中的x经检验可行,那(0,x)就一定不是最优解。满足二分答案的思路
1.注意如何保留2位小数 2.mid为0时不能做除数 3.left <= right
#include <cstdio>
#include <cmath>
const int maxn = 10000 + 5;
double small = 0.00000001;
int N, K;
int n[maxn];
int left, right,tmp, mid;
double enter;
int main()
{
scanf("%d %d", &N, &K);
for (int i = 0; i < N; i++) {
scanf("%lf", &enter);
n[i] = int((enter+small) * 100);
if (n[i] > right) right = n[i];
}
while(left <= right) {
mid = (left + right) / 2;
if (mid == 0) {
left = mid+1;
continue;
}
int count1 = 0;
for (int i = 0; i < N; i++) {
count1 += n[i] / mid;
}
if (count1 >= K) left = mid+1;
else right = mid-1;
}
printf("%.2f\n", right/100.0);
}
最大化最小值:同样满足二分的策略,取值落在一个区间,检测完一个值的可行性后就可以砍掉一半的区间, 那么那点在于如何对一个点进行检测对错
POJ2456 : 给出N个点,选C个位置令个点的最小距离最大。
keypoint:如何检测某点是否可行,sort后,从a[0]出发不断+d,看有几个。
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 5;
int N, C, left, right, mid;
int a[maxn];
bool test(int n) {
int counter = 1;
int last = a[0];
for (int i = 1; i < N; i++) {
if (last + n <= a[i]) {
counter++;
last = a[i];
}
}
if (counter >= C) return true;
else return false;
}
int main()
{
scanf("%d%d", &N, &C);
for (int i = 0; i < N; i++) {
scanf("%d", &a[i]);
}
sort(a, a+N);
left = 1; right = a[N-1] - a[0];
while (left <= right) {
int mid = (left + right) / 2;
if (test(mid)) left = mid + 1;
else right = mid - 1;
}
printf("%d\n", right);
}
三分法: 用于求单峰函数的极值,基于这么一个事实:
当 value(m1) > value(m2) 时 就可以将 m2 - right 砍掉, 虽然每次去掉1/3, 比二分少一点,但效率依旧好
ZOJ3203
#include <iostream>
#include <cstdio>
using namespace std;
#define eps 1e-8
double Left, Right, mid, dmid;
double midvalue, dmidvalue;
int cases;
double h, H, D, dx;
double calcu(double x)
{
if (x >= dx) return h*(D-x) / (H - h);
else return (h * D - H * x) / (D - x) + x;
}
void solve()
{
while (Left + eps < Right) {
mid = (Left * 2 + Right) / 3;
dmid = (Left + Right * 2) / 3;
midvalue = calcu(mid);
dmidvalue = calcu(dmid);
if (midvalue >= dmidvalue) Right = dmid;
else Left = mid;
}
}
int main()
{
cin >> cases;
while (cases--) {
scanf("%lf%lf%lf", &H, &h, &D);
dx = h * D / H;
Left = 0.0;
Right = D;
solve();
printf("%.3lf\n", calcu(Left));
}
}