ACM_二分(POJ1064)

1.二分法求解问题的这种思想其实十分的普遍。回忆一下我们在求一个方程的近似根的时候其实使用的就是二分的办法,当然这种求近似根的方法是有一定局限的,因为这样无法求不变号的零点,也就说哪种曲线刚刚好与坐标轴相切的零点我们通过普通的二分是无法求得的。但是,二分求近似根这种方法体现出来的二分思想其实是可以被用来解决很多的问题。

2.二分查找。如果我们在一个有序的数组里查找不大于某个数最大的下标,其实我们就可以通过二分来做,具体的代码在这里就不写了,思想和求根一样,不断的二分查找区间,直到找到为止。这样其实我们把复杂度下降到logn。

3.绳子的切割问题这个问题的大概意思就是给你一些绳子,和一个整数K,让你把这些绳子切割成K根长度相同的绳子并且使得这K根绳子尽可能的长。拿到这个问题,我们该如何分析呢?我们对最终的长度分析,设每一根根绳子的长度为x,那么我们可以知道的是,在长度为x的时候我们可以切出来的就是\sum L[i]/x,设这个数字就是num。也就是说:num=\sum L[i]/x。如果这个num>=K的,这代表什么意思呢?也就是说,现在K根长度为x,是可以切出来的,并且切出来之后的数量还是大于K的,说明这个最优解一定是大于x的,如果num<x呢,那就说明每一段的长度为x是不可行的,因为切出来的数量少了。当然我们可以遍历整个可能的x,一一验证,也就是枚举(或者说是暴力),但是这样的复杂度是远远不行的,观察到我们需要的长度是排列好的,也就谁有序的,而且解的排列也是排列在一条数轴上(注意这里面的含义,这是一个问题可不可以二分求解的一个重要的特诊)那么我们就可以二分整个区间求解。区间的下限当然是很好解决了,肯定是0嘛,上限呢?根据数据范围我们也可以很简单的确定下来。但是一般1要把这个二分的区间拉大一点,当然这里的拉大是适当的拉大。这样我们每次验证区间的重点,如果可行,更新左端点,不可行更新右端点。这样就可以很快的实现:
 

#include<iostream>
#include<cstdio>
#include<stdio.h>
#include<math.h>
#pragma warning(disable:4996)
using namespace std;
const int maxn = 1e4 + 10;
int N, K;
double len[maxn];
#define INF 2e5+10;
bool OK(double c)
{
    int num = 0;
    for (int i = 0; i < N; i++)
    {
        num += (int)(len[i] / c);
    }
    return num >= K;
}
void solve()
{
    double l = 0;
    double r = INF;
    for (int i = 0; i < 100; i++)
    {
        double mid = (l + r) / 2;
        if (OK(mid))l = mid;
        else r = mid;
    }
    printf("%0.2f\n", floor(r * 100) / 100);
}
int main()
{
    while (cin >> N >> K)
    {
        for (int i(0); i < N; i++)
            scanf("%lf", len + i);
        solve();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/83104175