二分 三分学习笔记

二分 三分学习笔记

前置

没有前置

二分

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

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

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

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

二分有两个模型,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的对应函数大小,看谁更大

适用与单峰函数

能用就行

posted @ 2019-08-03 00:09  wyswyz  阅读(105)  评论(2编辑  收藏  举报