二分

二分就是在某一个范围内,取中间的值进行判断的一种搜索方法。而且条件是要有一个连续的解空间。

写的时候需要注意的是循环结束条件的判断,要小心出现死循环。比如有的就要写成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\n");
		else
		printf("%.2f\n",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\n",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\n",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]='\0';
			}
			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\n",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\n",(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\n",an[n-1]);
		/*long long an=wei-sum[n-1];
		printf("%I64d\n",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("\n");
	}
	return 0;
}



 

 

在一个蛋糕上插蜡烛,一共有n个蜡烛。第i行可以插k的i次方个蜡烛,中间可以插一个或者不插

需要最小的r*k。

 

posted @ 2017-03-19 18:24  wyboooo  阅读(237)  评论(0编辑  收藏  举报