zoukankan      html  css  js  c++  java
  • 二分

    递增排序,递减排序,非递减排序,非递增排序

    1,2,3,4,5,.:递增排列
    9,8,7,6,5.:递减排列
    1,2,3,3,4,5,8,8,.:非递减排列
    9,8,7,7,6,5,5,2,1,.:非递增排列

    二分查找&&二分答案(部分内容参照二分查找和[ShawnZhou (https://www.luogu.org/blog/user20197/solution-p2678))

    使用二分需要满足两个条件。一个是有界,一个是单调。如果题目规定了有“最大值最小”或者“最小值最大”的东西,那么这个东西应该就满足二分答案的有界性(显然)和单调性(能看出来)。
    二分查找依赖于一个有序的集合。开始时,先找出有序集合中间的那个元素。如果此元素比要查找的元素大,就接着在较小的一个半区进行查找;反之,如果此元素比要找的元素小,就在较大的一个半区进行查找。在每个更小的数据集中重复这个查找过程,直到找到要查找的元素或者数据集不能再分割。
    查找数列中第一个大于某值的下标。也就是c++里的lower_bound()
    手写出来是这样的:

    int find(int a[],key,len)
    {
        int left=0;
        int right=len;
        int mid;
        while(left<=right)
        {
            mid=(left+right)>>1;
            if(a[mid]<key)
            {
                left=mid+1;
            }
            else
            {
                right=mid-1;
            }
        }
        return left;
    }
    

    二分答案的题:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define MAXN 100010
    using namespace std;
    
    int d,n,m;
    int a[MAXN];
    int l,ans,r,mid;
    
    bool check(int x)
    {
        int cnt=0;
        int i=0;
        int now=0;
        while(i<n+1)
        {
            i++;
            if(a[i]-a[now]<x)
            {
                cnt++;
            }
            else
            {
                now=i;
            }
        }
        if(cnt>m) return false;
        else return true;
    }
    
    int main()
    {
        scanf("%d%d%d",&d,&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        a[n+1]=d;
      	l=1;r=d;
        
        while(l<=r)
        {
    		mid=(l+r)>>1;
            if(check(mid))
            {
                ans=mid;
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
        }
        cout<<ans;
        return 0;
    }
    

    P1316 丢瓶盖P1824 进击的奶牛
    这道题和跳石头很像,不同的地方是题中所给的为留下多少而不是拿走多少,还有就是需要对坐标进行排序
    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define MAXN 1001000
    using namespace std;
    
    int n,k,b;
    int l,r,mid,ans;
    int a[MAXN];
    
    bool check(int x)
    {
    	int i=1;
    	int now=1;
    	int cnt=0;
    	while(i<n)
    	{
    		i++;
    		if(a[i]-a[now]<x)
    		{
    			cnt++;
    		}
    		else
    		{
    			now=i;
    		}
    	}
    	if(cnt>k) return false;
    	else return true;
    }
    int main()
    {
    	scanf("%d%d",&n,&b);
    	k=n-b;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    	}
    	sort(a+1,a+n+1);
    	l=1;
    	r=a[n];
    	while(l<=r)
    	{
    		mid=(l+r)>>1;
    		if(check(mid))
    		{
    			ans=mid;
    			l=mid+1;
    		}
    		else
    		{
    			r=mid-1;
    		}
    	}
    	cout<<ans;
    	return 0;
    } 
    

    P1873 砍树
    这道题除了要记得开long long 好像也没什么特别需要注意得了
    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    #define MAXN 20000010
    using namespace std;
    
    ll n,m;
    ll l,r,ans,mid;
    ll a[MAXN];
    
    bool check(ll x)
    {
        ll cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(a[i]>x)
            {
                cnt=cnt+(a[i]-x);
            }
        }
        if(cnt>=m) return true;
        else return false;
     } 
     
     int main()
     {
     	scanf("%lld%lld",&n,&m);
     	for(int i=1;i<=n;i++)
     	{
     		scanf("%lld",&a[i]);
     	}
     	sort(a+1,a+n+1);
     	l=1;
     	r=a[n];
     	while(l<=r)
     	{
     		mid=(l+r)>>1;
     		if(check(mid))
     		{
     			ans=mid;
     			l=mid+1;
             }
             else
             {
             	r=mid-1;
             }
        }
     	cout<<ans;
     }
    

    P1577 切绳子
    这道题因为小数的精度不够,所以小数的二分是另一种写法

    while(l+eps<=r)
        {
            mid=(l+r)/2.00000000;
            if(check(mid))
            {
                ans=mid;
                l=mid;
            }
            else r=mid;
        }
        int aa=(int)((ans+eps)*100);
        printf("%.2lf",1.0*aa/100.0);
    

    这道题除了用小数写也可以用整数写最后转化为小数
    这道题的AC代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define MAXN 100010
    #define eps 1e-8
    using namespace std;
    
    int n,k;
    double a[MAXN];
    double l,r,mid,ans;
    
    bool check(double x)
    {
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            int p=floor(a[i]/x) ;
            cnt=cnt+p;
        }
        if(cnt>=k) return true;
        else return false;
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%lf",&a[i]);
        }
        sort(a+1,a+n+1);
        l=0.00000000001;
        r=a[n];
        while(l+eps<=r)
        {
            mid=(l+r)/2.00000000;
            if(check(mid))
            {
                ans=mid;
                l=mid;
            }
            else r=mid;
        }
        int aa=(int)((ans+eps)*100);
        printf("%.2lf",1.0*aa/100.0);
        return 0;
    }
    

    P1843 奶牛晒衣服
    这道题如果每一步进行贪心的话会TLE,所以可以进行一些简单的优化
    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define MAXN 2000001
    #define ll long long
    using namespace std;
    
    ll n,a;
    double b;
    ll l,r,mid,ans;
    ll tot;
    ll A[MAXN],B[MAXN];
    
    bool check(ll x)
    {
    	for(int i=1;i<=n;i++)
    	{
    		A[i]=B[i];
    	}
    	for(int i=1;i<=n;i++)
    	{
    		A[i]=A[i]-a*x;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		tot=tot+A[i];
    	}
    	if((tot/b)>x) return false;
    	int cur=0;
    	for(int i=1;i<=n;i++)
    	{
    		if(A[i]>0&&A[i]<=b) cur++;
    		else if(A[i]>b)
    		{
    			cur=cur+ceil(A[i]/b);
    		}
    	}
    	if(cur>x) return false;
    	else return true;
    }
    
    int main()
    {
    	scanf("%lld%lld%lf",&n,&a,&b);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%lld",&A[i]);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		B[i]=A[i];
    	}
    	sort(A+1,A+n+1);
    	tot=0;
    	l=1;
    	r=A[n]/a+1;
    	while(l<=r)
    	{
    		mid=(l+r)>>1;
    		if(check(mid))
    		{
    			ans=mid;
    
    			r=mid-1;
    		}
    		else l=mid+1;
    	}
    	cout<<ans;
    	return 0;
    }
    
    

    P1918 保龄球

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #define MAXN 1000001
    #define M 100000000
    using namespace std;
    int a[MAXN];
    map<int,int> b;
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=1000000;i++)
        {
            if(a[i]==0) continue;
    //		cout<<i<<endl;
            b[a[i]]=i;
        }
    
        sort(a+1,a+n+1);
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int m;
            bool flag=0;
            scanf("%d",&m);
            int l=1,r=a[n];
            int mid;
            while(l<=r)
            {
    //			printf("l=%d,r=%d
    ",l,r);
                mid=(l+r)>>1;
    //			printf("mid=%d
    ",mid);
                if(mid==m)
                {
                    printf("%d
    ",b[m]);
                    flag=1;
    //				cout<<"aaa"<<endl;
                    break;
                }
                if(mid>m)
                {
                    r=mid-1;
    //				cout<<"bbb"<<endl;
                }
                if(mid<m)
                {
                    l=mid+1;;
    //				cout<<"ccc"<<endl;
                }
            }
            if(!flag)
            {
                printf("0
    ");
    //			cout<<"ddd"<<endl;
            }
        }
        return 0;
     } 
    
  • 相关阅读:
    python之enumerate
    Python中的集合set
    SGU 分类
    太空飞行计划 最大权闭合图
    1.飞行员配对 二分图匹配(输出方案)/最大流斩
    poj1149最大流经典构图神题
    hdu1569 方格取数 求最大点权独立集
    最大独立集 最小点覆盖 最小边覆盖 最小路径覆盖 最大团
    hdu3491最小割转最大流+拆点
    hdu3987,最小割时求最少割边数
  • 原文地址:https://www.cnblogs.com/LITTLESUNwl/p/11010765.html
Copyright © 2011-2022 走看看