二分

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

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;
 } 

转载于:https://www.cnblogs.com/LITTLESUNwl/p/11010765.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值