zoukankan      html  css  js  c++  java
  • 二分法(POJ-1064与POJ-2456)

    二分查找,简而言之就是在一个有序的序列中找一个元素,因为这些元素已经有序,所以每次都将要找的数跟待寻找序列的中间元素比较,如果要找的数大于中间元素,说明接下来只需要在该序列的右半边中找,所以可以不用管左半边了,这样每次都排除一半的元素直到最后找到要找的元素。我们可以用两个变量代表待寻找序列的最左端索引(l)和最右端索引(r),然后两个变量的中间元素是mid,再经过比较之后考虑将r移动到mid处成为下一次寻找的新的r值,还是将l值移动到mid+1处成为下一次寻找的l值。


    代码模板:

    while(l < r){
    		int mid = l + (r-l) / 2; //不直接用(l+r)/2是为了防止l+r的时候溢出 
    		if(judge(mid)) l = mid+1;  //judge函数是自己定义的,判断要找的数在序列的哪半边的
    		else r = mid;
    	}
    

    因为这样找的情况之下
    需要注意一个细节的关于边界的问题:假设要在100个排好序的数中找到某数(这个数的索引最后我们得知是8),最后这个while循环只会等到 l 和 r 两个数相等的时候才会停止,当循环停止的时候 ,并不知道上一个循环的lr分别是7和8,然后是l被赋值成为mid+1得到8的,还是l与r原本为8和9,然后r被赋值成为mid的,所以这个时候需要在最后再判断一下,因为循环结束的时候l和r已经相等了,所以他们指向的位置如果不是要找的数,那么l-1(或者说r-1)处的元素才是我们要找的元素


    下面给出二分法的例题

    POJ 1046
    https://vjudge.net/problem/POJ-1064
    最最简单的二分查找

    题意大概是有N根木棍,然后每一根都有着自己的长度,现在要求截断这些木棍,给出K根长度相等的木棍,问最长能截成多长(不能拼接)。

    那就直接用二分法做,从0到最长木棍的长度这样一直往最合适的点逼近即可,然后每次有了猜测的长度mid之后,就遍历所有木棍,然后将每一根木棍都截出尽可能多小木棍,也就是直接 / mid ,然后加到总数中啦。

    但是要注意一个特殊的地方,就是除零的时候可能出错(在下方代码中也标出来了)

    #include<cstdio>
    #include<iostream>
    #include<vector>
    #include<cstring>
    #include<algorithm> 
    using namespace std;
    const int maxn = 100500;
    int N,K,k,ma = 0;
    double b,ans;
    int c[maxn];
    
    bool judge(int mid, int k){
    	int sum = 0;
    	if(mid == 0) return 0;  //这里如果不直接返回0的话可能在下下行 /mid的时候发生除零错
    	for(int i=0;i<N;i++){
    		sum += c[i] / mid;
    		if(sum >= k) return 1;
    	}
    	return 0;
    }
    
    void binsearch(int l,int r,int k){
    	while(l<r){
    		int mid = (r-l)/2+l;
    		if(judge(mid,k)) l = mid+1;
    		else r = mid;
    	}
    	if(judge(l,k)) ans = double(l) / 100.0;
    	else ans = double(l-1) / 100.0;
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
    	//freopen("in.txt","r",stdin);
    #endif
    	scanf("%d%d",&N,&K);
    	for(int i=0;i<N;i++){
    		scanf("%lf",&b);
    		c[i] = int(b * 100);
    		if(c[i] > ma) ma = c[i];
    	}
    	sort(c,c+N,greater<int>());
    	binsearch(0,c[0],K);
    	if(ans < 0.01) ans = 0.00;
    	printf("%.2f
    ",ans);
    	return 0;
    }
    

    稍难一点的题是 POJ-2456
    https://vjudge.net/problem/POJ-2456


    题意比较简单,就是有N个点在数轴上排成一横排,你要从中取C个点使得这C个点中任意两个点之间的最短距离最大。首先看到任意两个点之间的最短距离,那这个距离肯定从相邻i啷个点之间的距离中来选出,因为如果a,b,c三个点依次从左到右排开,也就是说ab两个点相邻而ac两个点不相邻,那么ac之间的距离肯定大于ab。

    然后这个题就是我们说到的最大化最小值的问题,因为它要满足两个要求(又要是所有间距中最小,又要是所有分配方案中最大)而贪心基本上除了一些特殊的处理(比如poj2010等等)只能满足往一个条件去贪,这样就比较难做。




    但是二分就可以解决这个问题,毕竟是要找一个数嘛,那么我们其实还是一个一个地去尝试,只不过二分了之后可以每次试玩排除一半的数,这样就降低了复杂度。


    基本思路就是先取l为最小距离(一般就是0啦),然后再取任意两点之间距离的最大值(也就是第一个点和最后一个点之间的距离),然后我们的所有的值就在这中间找啦。每次判断中间值是否符合要求,因为我们要找的是尽量大,所以一旦符合要求,我们就删掉左半边序列,往右半边去找,反之才往左半边去找,那么怎么才能够判断是否符合要求呢?


    既然这个时候中间值mid是我们猜测的一个解,那么首先它需要满足它是所有间隔中的最小值,我们就所以任意间隔都要比他大,我们从最左边的点开始,依次遍历所有点,然后每当凑到距离>= mid了之后,就表示我们可以选这个遍历了的点,然后继续往下走,如果凑到了C个点,就说明这个mid是符合要求的,(反之如果所有点都遍历完了,还没有凑到C个点,就说明该mid不符合要求,不是我们要的解),如果符合要求了,那么再到它的右边去找,(上述操作就是代码中的judge过程,也就是判断中间这个数能否满足我们的基础要求(即所有间距中的最小值),二分的过程就是找这些值中的最大)


    #include<cstdio>
    #include<iostream>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    #include<set> 
    using namespace std;
    const int maxn = 100150;
    int N,C;
    int a[maxn];
    bool judge(int x){
    	int cur = a[0],num = 1;
    	for(int i=1;i<N;i++){
    		if(a[i] - cur >= x) {
    			num++;
    			cur = a[i];
    		}
    		if(num >= C) return true;
    	}
    	return false;
    }
    
    void solve(){
    	int l = 0,r = a[N-1] - a[0];
    	while(l < r){
    		int mid = l + (r-l) / 2; //不直接用(l+r)/2是为了防止l+r的时候溢出 
    		if(judge(mid)) l = mid+1;
    		else r = mid;
    	}
    	if(judge(r)) cout<<r<<endl;
    	else cout<<l-1<<endl;
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("in.txt","r",stdin);
    #endif
    	scanf("%d%d",&N,&C);
    	for(int i=0;i<N;i++){
    		scanf("%d",&a[i]);
    	}
    	sort(a,a+N);
    	solve();
    	return 0;
    }
    
  • 相关阅读:
    linux cpu load学习笔记
    P1064 金明的预算方案
    P1757 通天之分组背包
    P1352 没有上司的舞会
    P1651 塔
    P1250 种树
    P1938 [USACO09NOV]找工就业Job Hunt
    P4392 [BOI2007]Sound 静音问题
    P3884 [JLOI2009]二叉树问题
    P2880 [USACO07JAN]平衡的阵容Balanced Lineup
  • 原文地址:https://www.cnblogs.com/kevin-matrix/p/14484621.html
Copyright © 2011-2022 走看看