二分答案、一个非常优秀的$logn$算法。

顾名思义、二分答案就是通过不断地二分寻找答案。
也就是找最中间的点。

二分答案与二分的基本条件一样、但是二分答案通常需要check函数、表示检查mid值是否符合题目要求。以此来找到最优解。

而r与l的差通常使得二分答案难于写出、这里推荐使用r-l>= 5然后通过题目描述分别测试这5个数那个更符合要求。而check函数因人而异。

让我们来找一道习题练练手。

书的复制

我们首先要确认二分答案啥.
这道题我们需要二分复制时间。

最后的答案需要保证后面多写、前面少写。
因为题目中说尽量前面的少取

Code

// Problem: 书的复制

#include <iostream>
#include <cstdio>

using namespace std;

const int MAXN = 501;

int a[MAXN + 1];

int n, m;
    
bool check(int x)
{
    int cnt = 0, k = 1;
    for (int i = 1; i <= n; i ++ )
    {
        if (a[i] > x)
        {
            return false;
        }
        if (cnt + a[i] > x)
        {
            k ++;
            cnt = a[i];
        }
        else
        {
            cnt += a[i];
        }
    }
    return k <= m;
}

pair<int, int> res[10001];

void getans(int x)
{
    int k = m, f = 0;
    res[k].first = n;
    for (int i = n; i >= 1; i -- )
    {
        f += a[i];
        if (f > x)
        {
            f = a[i];
            res[k].second = i + 1;
            k --;
            res[k].first = i;
        }
    }
    res[1].second = 1;
    for (int i = 1; i <= m; i ++ )
    {
        printf("%d %d\n", res[i].second, res[i].first);
    }
}

int main()
{
    int l, r;
    scanf("%d%d", &n, &m);
    l = 1;
    r = 100001;
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &a[i]);
    }
    while(r - l > 5)
    {
        int mid = (r + l) / 2;
        if (check(mid))
        {
            r = mid;
        }
        else
        {
            l = mid;
        }
    }
    int res = 0;
    for (int i = l; i <= r; i ++ )
    {
        if (check(i))
        {
            res = i;
            break;
        }
    }
    getans(res);
    return 0;
}

Last modification:August 25th, 2020 at 08:11 pm
End.