二分

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

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\n",l,r);
            mid=(l+r)>>1;
//			printf("mid=%d\n",mid);
            if(mid==m)
            {
                printf("%d\n",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\n");
//			cout<<"ddd"<<endl;
        }
    }
    return 0;
 } 
posted @ 2019-06-12 17:01  LITTLESUN_wl  阅读(235)  评论(0)    收藏  举报