zoukankan      html  css  js  c++  java
  • 二分 三分学习笔记

    二分 三分学习笔记

    前置

    没有前置

    二分

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

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

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

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

    二分有两个模型,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
    ", ans);
    	for(int i = 1; i <= n; i++)
    	{
    		if(vis[i] == 0) vis[i] = d;
    		printf("%lld
    ", 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的对应函数大小,看谁更大

    适用与单峰函数

    能用就行

  • 相关阅读:
    [Windows] 重新安装/卸载桌面版OneDrive / Reinstall/ Uninstall Desktop Version OneDrive
    [Linux] 关闭防火墙以及开放端口
    [Java] Properties类
    [Linux] 文档编辑搜索
    [Dababase
    etymological
    [JavaScript] 表单验证不通过不提交的JS写法
    Lyrics来源
    [Maven
    [ Servlet / JSP ] J2EE Web Application 中的 JSESSIONID 是什么?
  • 原文地址:https://www.cnblogs.com/wyswyz/p/11291808.html
Copyright © 2011-2022 走看看