二分答案
在昨天的日记里,我们粗略的谈了谈二分。今天,我们来讲讲二分的一个分支——二分答案。
从宏观的角度来讲,二分答案分为整数二分和实数二分。二者的区别就是二分的区间是整数还是实数而已。
整数二分是二分答案的基础,其形式多样且较为简单。下面以最小值最大为例,推荐一种笔者觉得很好的方法。
while (l<=r){
mid=(l+r)>>1;
if (check(mid)){
ans=mid;
l=mid+1;
}
else r=mid-1;
}
二分出来的结果就是 a n s ans ans。而且如果求最大值最小的话,只需要把 l l l和 r r r的修改互换一下即可。
实数二分昨天略讲了一下,需要注意的地方注意到了,一般实数二分也就那样了。这里一样以最小值最大为例,给出两种模板。
方法1:固定精度法
while (r-l>=eps){
//ops随题目而变化
mid=(l+r)/2;
if (check(mid))
l=mid;
else r=mid;
}
---------------------
方法2:固定次数法
for(int i=1;i<=100;i++){
//次数随题目而变化
mid=(l+r)/2;
if (check(mid))
l=mid;
else r=mid;
}
如果精度足够高(如保留到题目要求后 2 − 3 2-3 2−3位),答案是 l l l是 r r r无太大所谓。
回到最宏观的角度,二分答案(无论是整数二分还是实数二分),难度都在check()
函数上。而check()
一般要配合其它算法计算。
洛谷P3718
【题目】: 有
N
N
N盏灯排成一列,其中有些灯开着,有些灯关着。小可可
希望灯是错落有致的,他定义一列灯的状态的不优美度为这些灯中最长的连续的开着或关着的灯的个数。小可可
最多可以按开关
k
k
k次,每次操作可以使该盏灯的状态取反:原来开着的就关着,反之开着。现在给出这些灯的状态,求操作后最小的不优美度。
【代码】:
const int N=100100;
int l,r,mid,ans,n,k,tot;
int c[N],p[N];char s[N];
inline void calc_p_and_tot(){
p[tot=1]=1;
for(int i=2;i<=n;i++)
if (s[i]==s[i-1])
p[tot]++;
else p[++tot]=1;
}
inline bool pd(){
for(int i=1;i<=n;i++)
if (s[i]=='N')
c[i]=1;
else c[i]=0;
register int cnt,tot,ans;
cnt=1;tot=ans=0;
for(int i=1;i<=n;i++){
if (cnt!=c[i]) tot++;
if (1-cnt!=c[i]) ans++;
cnt=1-cnt;
}
if (tot<=k) return true;
if (ans<=k) return true;
return false;
}
inline bool check(int mid){
if (mid==1)
return pd();
register int cnt=0;
for(int i=1;i<=tot;i++)
if (p[i]>mid)
cnt+=p[i]/(mid+1);
return cnt<=k;
}
int main(){
scanf("%d%d",&n,&k);
scanf("%s",s+1);
l=1;r=n;ans=-1;
calc_p_and_tot();
while (l<=r){
mid=(l+r)>>1;
if (check(mid)){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d",ans);
return 0;
}
洛谷P3743
【题目】: kotori
有
n
n
n 个可同时使用的设备。第
i
i
i 个设备每秒消耗
a
i
a_i
ai个单位能量。能量的使用是连续的,也就是说能量不是某时刻突然消耗的,而是匀速消耗。也就是说,对于任意实数 ,在
k
k
k 秒内消耗的能量均为
k
×
a
i
k\times a_i
k×ai 单位。在开始的时候第
i
i
i 个设备里存储着
b
i
b_i
bi个单位能量。
同时 kotori
又有一个可以给任意一个设备充电的充电宝,每秒可以给接通的设备充能
p
p
p 个单位,充能也是连续的,不再赘述。你可以在任意时间给任意一个设备充能,从一个设备切换到另一个设备的时间忽略不计。
kotori
想把这些设备一起使用,直到其中有设备能量降为
0
0
0。所以 kotori
想知道,在充电器的作用下,她最多能将这些设备一起使用多久。
【代码】:
const int N=100100;
double l,r,mid,ans;
int a[N],b[N],p,n;
inline bool check(double mid){
register double sum=0;
for(int i=1;i<=n;i++)
sum+=max(0.0,a[i]*mid-b[i]);
return sum<=p*mid;
}
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
const double eps=1e-7;
int main(){
n=read();p=read();
for(int i=1;i<=n;i++){
a[i]=read();
b[i]=read();
ans+=a[i];
}
if (ans<=p){
printf("-1");
return 0;
}
l=0.0;r=1e10;
while (r-l>=eps){
mid=(l+r)/2;
if (check(mid))
l=mid;
else r=mid;
}
printf("%.6lf",l);
return 0;
}
注意:精度不能再小,否则会TLE
;
r
r
r不能再小,否则会WA
!!!