二分就是在某一个范围内,取中间的值进行判断的一种搜索方法。而且条件是要有一个连续的解空间。
写的时候需要注意的是循环结束条件的判断,要小心出现死循环。比如有的就要写成ed-st>1而不是>0否则就会出现死循环了。
A - Can you solve this equation?
找到一个0-100内满足方程的实数解。二分这个解就可以了。循环结束的条件是ed-st>0.000001
B - Cable master
把好几段绳子切成至少n段,问每段绳子最长能有多长。
这道题超级坑,觉得自己很快就要A了以后,把所有bug都改了,写到十二点交上去结果T了。差点吐血然后就去睡了,第二天从g++换成了c++提交上去就过了。
在0-最长的长度之间二分结果,算这个长度可以切的段数。这道题还有精度要限制,所以最后输出用了floor
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int main()
{
int n,k;
double ca[10005];
while (scanf("%d%d",&n,&k)!=EOF)
{
int i;
long long cnt=0;
for(i=0;i<n;i++)
{
cin>>ca[i];
ca[i]+=0.000001;
}
sort(ca,ca+n);
double ed,st,mid;
st=0.00;
ed=ca[n-1];
while((ed-st)>1e-8)
{
cnt=0;
mid=st+(ed-st)/2;
for(i=0;i<n;i++)
{
cnt+=(int)(ca[i]/mid);
}
if(cnt>=k)
st=mid;
else if(cnt<k)
ed=mid;
}
if(mid<0.01)
printf("0.00
");
else
printf("%.2f
",floor(mid*100)/100);
}
return 0;
}
C - Aggressive cows
有n个坐标要放下n头牛,问他们之间最大可以间隔多少可以放下这么多牛
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
int n,c,dis[100005];
bool ok(int m)
{
int last=0;
for(int i=1;i<c;i++)
{
int cur=last+1;
while(cur<n&&dis[cur]-dis[last]<m)//先将一头牛放在dis[last]的位置上,然后把下一头牛放在下一个满足条件的位置上
cur++;
if(cur==n)
return false;//不能放下c头牛,就直接返回false
last =cur;
}
return true;
}
int main()
{
while (scanf("%d%d",&n,&c)!=EOF)
{
int i;
for(i=0;i<n;i++)
scanf("%d",&dis[i]);
sort(dis,dis+n);
int st,ed,mid;
st=dis[0];
ed=dis[n-1];
while(ed-st>1)
{
mid=st+(ed-st)/2;
if(ok(mid))
st=mid;
else ed=mid;
}
printf("%d
",st);
}
return 0;
}
D - The Meeting Place Cannot Be Changed
有n个朋友,每个人有自己的速度,问最少需要多少时间这些人可以在一点相遇
先给这些人按坐标从小到大排序,在0-10000000000之间二分找到那个时间
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
struct node{
int x;
int v;
};
struct node fri[60005];
bool cmp(node a,node b)
{
if(a.x<b.x)
return true;
else return false;
}
bool find(double mid)
{
int i;
double x1=fri[0].x-mid*fri[0].v;
double x2=fri[0].x+mid*fri[0].v;
for(i=1;i<n;i++)//找这些人的范围有没有交集
{
double temp1=fri[i].x-fri[i].v*mid;//第i个人的左端点
double temp2=fri[i].x+fri[i].v*mid;//第i个人的右端点
if(temp1>x1)
x1=temp1;
if(temp2<x2)
x2=temp2;
if(x1>x2)
return false;
}
return true;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int i;
for(i=0;i<n;i++)
{
scanf("%d",&fri[i].x);
}
for(i=0;i<n;i++)
{
scanf("%d",&fri[i].v);
}
sort(fri,fri+n,cmp);
double st,ed;
st=0;
ed=10000000000;
while((ed-st)>0.000001)
{
double mid=st+(ed-st)/2;
if(find(mid))
ed=mid;
else
st=mid;
}
printf("%.10f
",ed);
}
return 0;
}
E - String Game
输入一个字符串,按顺序删除某个位置的字符,问最多删除多少个字符可以得到要求的字符串
按照给的那个顺序,倒过来往一个空字符串里加字符,二分加入的个数。
#include<iostream>
#include<cstring>
#include<stdio.h>
using namespace std;
int main()
{
char p[200005],t[200005],s[200005];
int de[200005];
while (scanf("%s%s",p,t)!=EOF)
{
int lenp=strlen(p);
int i,j;
for(i=0;i<lenp;i++)
{
scanf("%d",&de[i]);
}
int st,ed,mid;
bool flag;
st=0;
ed=lenp;
while((ed-st)>0)//向s数组里反向添加字符
{
flag=false;//判断s中是否包含t
mid=(ed+st)/2; //mid为添加的个数
for(i=0;i<mid;i++)
{
s[de[lenp-1-i]-1]=p[de[lenp-1-i]-1];
}
j=0;
for(i=0;i<lenp;i++)
{
if(s[i]==t[j])
{
j++;
}//判断s中包不包含t的字符
}
if(j>=(int)strlen(t))
{
flag=true;
}
for(i=0;i<lenp;i++)
{
s[i]=' ';
}
if((ed-st)==1)
{
if(flag)
break;
else
{
mid +=1;
break;
}
}
else
{
if(flag)
ed=mid;
else
st=mid;
}
}
cout<<lenp-mid;
}
return 0;
}
F - Yukari's Birthday
在一个蛋糕上插蜡烛,一共有n个蜡烛。第r行可以插k的r次方个蜡烛,中间可以插一个或者不插
需要最小的r*k。
经过计算r不会太大,所以可以枚举r,再二分k
如果每一层的蜡烛数都计算再相加,有可能会超出longlong 所以当计算出超过n的时候就可以break了
#include<stdio.h>
#include<iostream>
#include<cmath>
using namespace std;
long long n;
int main()
{
while(scanf("%lld",&n)!=EOF)
{
long long r,minn=n-1,anr=1,ank=n-1,s1=0,s2=0,s=0;
for(r=1;r<=40;r++) //50
{
long long st=1,ed,mid;
ed=n+1-r;
while(ed-st>1)
{
mid=st+(ed-st)/2;
s=0;
int flag=1;
for(int i=1;i<=r;i++)
{
s+=pow(mid,i);
if(s>n||s<0) //if(s>n-1||s<0)
{
flag =0;
break;
}
}
if(s==n||s==n-1)
{
if(r*mid<minn)
{
anr=r;
ank=mid;
}
}
if(flag)
st=mid;
else
ed=mid;
}
}
printf("%lld %lld
",anr,ank);
}
return 0;
}
H - Monthly Expense
有n天要分成m个阶段,每天都有各自的费用,把连续的天归为一个阶段。要算出各个阶段最少的费用中最大的费用数。
在0-最大的一天的费用之间二分每个阶段最大的费用。计算此时可以分的阶段,来改变st和ed
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,m,exp[100005];
/*bool find(double mid)
{
int i,mon=0,sum=0;
for(i=0;i<n;i++)
{
sum+=exp[i];
if(sum>mid)
{
mon++;
sum=exp[i];
}
}
if(mon>m)
return true;
else return false;
}*/
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
int i,maxn=0,s=0;
for(i=0;i<n;i++)
{
scanf("%d",&exp[i]);
maxn=max(maxn,exp[i]);
s+=exp[i];
}
/*int max=exp[0];
for(i=1;i<n;i++)
{
if(exp[i]>max)
max=exp[i];
}*/
double st,ed;
st=maxn;
ed=s;
while (ed-st>0)
{
double mid=st+(ed-st)/2;
int i,mon=0,sum=0;
for(i=0;i<n;i++)
{
sum+=exp[i];
if(sum>mid)
{
mon++;
sum=exp[i];
}
}
if(mon<m)
ed=mid;
else st=mid+1;
}
printf("%d
",(int)st);
}
return 0;
}
J - Cow Acrobats
有n头牛,每头牛有他的weight和strength。这些牛要搭成一座塔。在第i头牛之上的所有牛的体重减去第i头牛的strength就是他的risk
要找到risk最小的那种排列之中最大的那个risk
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
long long n,an[50005];
struct cow{
int w;
int s;
long long sum;
}a[50005];
bool cmp(cow a,cow b)
{
return a.sum<b.sum;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int i;
for(i=0;i<n;i++)
{
scanf("%d%d",&a[i].w,&a[i].s);
a[i].sum=a[i].w+a[i].s;
}
sort(a,a+n,cmp);//按sum从小到大排序
long long ans=0;
an[0]=ans-a[0].s;
for(i=1;i<n;i++)
{
ans+=a[i-1].w;//i头牛上面的体重
an[i]=ans-a[i].s;//第i头牛的risk
}
sort(an,an+n);
printf("%I64d
",an[n-1]);
/*long long an=wei-sum[n-1];
printf("%I64d
",an);
int st,ed,mid;
st=0;
ed=n-1;
long long min;
while(ed-st>1)
{
mid=st+(ed-st)/2;
min=min(min,wei-w[i]-v[i]);
}*/
}
return 0;
}
用数学公式计算,可以得到第i头牛的risk就是这i个牛所有的weight加起来再减去它的weight和strength。
把每头牛的weight和strength加起来,最小的应该放在最上面。所以按这个排个序,把每头牛的risk记下来,输出最大的那个就可以了。感觉好像和二分没什么关系
L - K Best
有n个珠宝,每个有value和weight,要留下k个s最大的。问那几个是要留下的。
因为要排序,所以用结构体存,把这个珠宝的位置也存下来。
二分这个s,作为平均价值。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define INF 1000001
using namespace std;
int n,v[100005],w[100005],t;
struct an{
double r;
int k;
}m[100005];
bool cmp(an a,an b)
{
return a.r>b.r;
}
bool find(double x)
{
int i;
for(i=0;i<n;i++)
{
m[i].r=v[i]-x*w[i];
m[i].k=i+1;
}
sort(m,m+n,cmp);
double sum=0;
for(i=0;i<t;i++)
{
sum+=m[i].r;
}
return sum>=0;
}
int main()
{
while (scanf("%d%d",&n,&t)!=EOF)
{
int i;
for(i=0;i<n;i++)
{
scanf("%d%d",&v[i],&w[i]);
}
double st=0,ed=INF,mid;
//while(ed-st>1e-8)
for(i=0;i<50;i++)
{
mid=st+(ed-st)/2;
if(find(mid))
st=mid;
else
ed=mid;
}
printf("%d",m[0].k);
for(i=1;i<t;i++)
{
printf(" %d",m[i].k);
}
printf("
");
}
return 0;
}
在一个蛋糕上插蜡烛,一共有n个蜡烛。第i行可以插k的i次方个蜡烛,中间可以插一个或者不插
需要最小的r*k。