二分 三分学习笔记

二分 三分学习笔记

前置

没有前置

二分

二分通常运用在题目描述求最小的最大值或最大的最小值

具体思想就是不断将一个区间分成两段,取合法的一段继续分两段

不断的接近一个极小区间,这个极小区间就是答案

但通常一个点要么合法要么不合法,不用一直分下去

二分有两个模型,1100型和0011型(1合法,0不合法)

一道简单的例题体会一下

P1024 一元三次方程求解

这道题是典型0011型,以两个点乘积小于零为一段区间的合法条件

如果mid ~ r 合法 l = mid

否则r = mid

然后二分时卡一下精度即可

代码

#include <cstdio>
#include <algorithm>
#define maxn 500010

using namespace std;

const double eps = 1e-4;

double a, b, o, d, mid;

double c(double x)
{
    return x * x * x * a + x * x * b + x * o + d;
}

int main() {
    scanf("%lf%lf%lf%lf", &a, &b, &o, &d);
    double l, r;
    for(int i = -100; i <= 100; i++)
    {
        l = i;
        r = i + 1;
        if(c(l) == 0) 
        {
            printf("%.2f ", l);
            continue;
        }
        if(c(l) * c(r) < 0)
        {
            while(r - l > eps)//卡精度
            {
                mid = (l + r) / 2;
                if(c(mid) * c(r) <= 0) l = mid;//合法or不合法
                else r = mid;
            }
            printf("%.2f ", l);
        }
    }
    return 0;
}

难一点的

P1182 数列分段 Section II

二分主要难在判断是否合法

这道题我们 mid 为当前最大值

如果以此最大值分出的组数sum <= m则合法

否则不合法

因此此题为0011型

#include <cstdio>
#include <algorithm>
#define maxn 500010

using namespace std;

int l, r, n, m, a[maxn], now, sum;

bool pd(int mid)
{
    now = mid;
    sum = 1;//初始化
    for(int i = 1; i <= n; i++)
    {
        if(now < a[i])//如果现在的组数数值最大值不够a[i],就开一个新的组
        {
            sum++;
            now = mid - a[i];
        }
        else now -= a[i];
    }
    return sum <= m;
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        l = max(l ,a[i]);
        r += a[i];
    }
    int mid;
    while(l < r)
    {
        mid = (l + r) >> 1;
        if(pd(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%d", l);
    return 0;
}

P2985 吃巧克力Chocolate Eating

此题mid指最不开心的一天的开心值

判断时枚举每一天,当今天开心值小于mid时就吃下一颗巧克力,如果巧克力吃完了还小于了mid就说明不合法

要注意的是找最小开心值时不能记录当前吃巧克力顺序,因为程序会把所有可能的情况都记下来

我们应在最后再跑一遍pd,把这时候的顺序记下来即可

#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 50010
typedef int int_ ;
#define int long long

using namespace std;

int val[maxn], n, d, l, r, vis[maxn], ans;

bool pd(int mid)
{
    int now = 0, last = 0;
    for(int j = 1; j <= d; j++)
    {
        last /= 2;
        while(last < mid)
        {
            last += val[++now];
            if(now > n) return 0;
            if(mid && mid == ans)vis[now] = j;
        }
    }
    return 1;
}

int_ main() {
    scanf("%lld%lld", &n, &d);
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld", &val[i]);
        r += val[i];
    }
    int l = 0;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(pd(mid)) 
        {
            l = mid + 1;
            ans = mid;
        }
        else r = mid - 1;
    }
    pd(ans);//最后对答案跑一遍记录顺序
    printf("%lld\n", ans);
    for(int i = 1; i <= n; i++)
    {
        if(vis[i] == 0) vis[i] = d;
        printf("%lld\n", vis[i]);
    }
    return 0;
}

三分

#include <cstdio>
#include <algorithm>
#define maxn 500010

using namespace std;

const double eps = 1e-7;

int n;
double l, r, xs[110];

double f(double x)
{
    double ret = 0;
    for(int i = 0; i <= n; i++)
    {
        double cf = 1;
        for(int j = 1; j <= i; j++) cf *= x;
        ret += xs[i] *cf;
    }
    return ret;
}

int main() {
    scanf("%d%lf%lf", &n, &l, &r);
    for(int i = n; i >= 0; i--) scanf("%lf", &xs[i]);
    while(r - l > eps)
    {
        double mid = (l + r) / 2.0;
        double mmid = (mid + r) / 2.0;
        if(f(mid) > f(mmid)) r = mmid;
        else l = mid;
    }
    printf("%.5lf", l);
    return 0;
}

就如题目。。。三分

就不断比较mid和mmid的对应函数大小,看谁更大

适用与单峰函数

能用就行

猜你喜欢

转载自www.cnblogs.com/wyswyz/p/11291808.html