zoukankan      html  css  js  c++  java
  • 二分

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

    写的时候需要注意的是循环结束条件的判断,要小心出现死循环。比如有的就要写成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。

  • 相关阅读:
    Java实现 LeetCode 421 数组中两个数的最大异或值
    Java实现 LeetCode 421 数组中两个数的最大异或值
    Java实现 LeetCode 421 数组中两个数的最大异或值
    Java实现 LeetCode 420 强密码检验器
    Java实现 LeetCode 420 强密码检验器
    Linux系统Wpa_supplicant用法小结
    wpa_supplicant 和 802.11g WPA 认证的配置
    wpa_supplicant无线网络配置
    LINUX系统中动态链接库的创建与使用{补充}
    LINUX系统中动态链接库的创建与使用
  • 原文地址:https://www.cnblogs.com/wyboooo/p/9643464.html
Copyright © 2011-2022 走看看