二分
先举一个例子:肯定很多人小的时候都玩过猜数字这个游戏,那么现在问题来了,如果在1-1000中去进行猜数字的游戏,那么我们最多要猜多少次,一些心急的人会说到1000次,如果按照顺序的方法去进行游戏的话,显然是1000次的,但是我们在实际操作中,真的有按照从1到1000的顺序去进行猜数字吗?肯定是不会的(除非你傻)。因为我们每一次得到的回答都是我们询问的数字与答案之间的大小关系,所以如果我们按照这个思路去进行二分的话,在最坏的情况下至少会猜10次,也就是log21000;具体的方法请看下面的代码。
int middle(void)
{
int l=1;r=1001;
while (l+1<r)
{
mid=(l+r)/2;
if (mid<=ans) r=mid;
else l=mid+1;
}
return l;
}
我们可以把二分的模型抽象的看成是一个长度为n的01数组,那么我们可以发现这个01数组一定是单调递增的:000000000000011111111111……我们每一次通过二分可以访问一个位置,我们要求第一个1的下标,那么问我们最少要去访问几次,那么很显然时间复杂度就是O(k log n)的,k是每次访问的代价。
于是乎例子的问题就可以转换为是一个长度1000的数组,如果下标≤答案的就填成是0,而>答案的就填充为1,那么这个数组是单调递增的,而每次访问的代价是常数的时间。
从这里我们就可以发现了二分的作用就是将时间复杂度优化,他可以将一个O(n)的算法变成是O(log n)的,他实现的思想就是每一次都把答案的范围缩小一半,知道最后只剩下一个答案为止。同时,二分是符合单调性的。
我们通过二分答案,花费一个log的代价,将最优化的问题转化成了判定性的问题(这里有一点概念性)。
我们看一下二分的经典问题:有n个数字,要求分成相邻的k组,使得每组数字之和的最大值最小,保证答案不超过10的9次方。(每个数都为非负整数)
我一眼看下来,完全看不到正解(我太菜了),于是我果断打了一个暴力。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int maxx,n,k,a[1010];
bool tf[1010];
void dg(int x,in