二分 三分学习笔记
前置
没有前置
二分
二分通常运用在题目描述求最小的最大值或最大的最小值
具体思想就是不断将一个区间分成两段,取合法的一段继续分两段
不断的接近一个极小区间,这个极小区间就是答案
但通常一个点要么合法要么不合法,不用一直分下去
二分有两个模型,1100型和0011型(1合法,0不合法)
一道简单的例题体会一下
这道题是典型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;
}
难一点的
二分主要难在判断是否合法
这道题我们 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;
}
此题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的对应函数大小,看谁更大
适用与单峰函数
(能用就行)