二分算法基础 第一章 简单二分

二分算法是一种效率极高的算法(思想),在很多的实例中都得以运用,比如快排和归并排序。本文将对二分算法的一些基础内容以及使用进行讨论。

入门-二分查找
我们先来看一个二分查找的例子。(p2searching.cpp)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
int p2s(int *a,int l,int r,int t)
{
    int mid=(l+r)/2;
    if(a[mid]==t) cout<<"FIND!"<<endl;
    if(l==r) return 0;
    if(a[mid]>t) p2s(a,l,mid,t);
    if(a[mid]<t) p2s(a,mid+1,r,t);
    return 0;
}
int main()
{
    int n,a[1000],i,t;
    cin>>n;
    for(i=0;i<n;i++)
        cin>>a[i];
    sort(a,a+n);
    cin>>t;
    p2s(a,0,n-1,t);
    return 0;
 } 

这是一个典型的(简单的)例子,输入5 1 3 2 4 5之后,我们可以用二分原理来查找一个对应的元素,如果元素在其中,就会输出FIND!!!。
(注:事实上这题采用大数组标记的方法会非常方便,在这里之所以使用二分查找,是为了构建一个二分查找程序的框架,来为后面的问题打下基础。)
让我们来测试一下效率,稍微修改一下程序,将输出find改为输出成功计数,修改数据范围,测试可得,在数据大小为20000的情况下,可在1s内完成10M-30M次查找,应当说效率是相当高的。(程序见p2searchingex.cpp)

初级-用二分代替简单的暴力枚举
很多简单的暴力枚举,其实可以用二分替代来加快效率。事实上,对于二分查找的问题,框架基本同上,只需要把对数据的处理过程加入到p2s函数中即可。
例如,我们现在用二分方法来解决一下巧克力问题。
佳佳邀请了M个同学到家里玩。为了招待客人,她需要将巧克力分给她的好朋友们。她有N(1<=N<=5000)块巧克力,但是大小各不一样,第i块巧克力大小为为1*Xi(1<=i<=N,1<=Xi<=10000),如下图。

为了公平,她需要让每人所分得的巧克力大小一样,而且为了礼貌,巧克力是一整块地分给客人。所以她需要将巧克力分成大小为S的M块,而且使得S最大。但她很忙还要照顾她的客人,所以就将任务交给你了,你需要求出S。
[输入文件]
第一行,N,M
下接N行为N块巧克力的大小
[输出文件]
仅有一行,为巧克力大小S。
事实上,对于这样的数据范围,用暴力枚举也不为过。但是,如果数据量加大一个数量级……
我在一台虚拟机上对用暴力法写的程序进行了评测,结果按照搜索顺序分别为0.2秒和0.5秒。显然,这个时间是很长的。
因此,我们使用二分来重新解决这个问题。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define _DEBUG_CONS_NT
using namespace std;
int ans=999,m,n,a[100005];
int p2s(int l,int r)
{
    int mid=(l+r)/2,sum=0;
    if(l==r)
        return 0;
    for(int i=0;i<n;i++)
        sum+=a[i]/mid;
    if(sum>=m)
        ans=mid,
        p2s(mid+1,r);
    else
        p2s(l,mid);
    return 0;
}
int main()
{
    #ifndef _DEBUG_CONS_NT
    freopen("chocolate.in","r",stdin);
    freopen("chocolate.out","w",stdout);
    #endif
    int i,j,t=0;
    cin>>n>>m;
    for(i=0;i<n;i++) 
        scanf("%d",&a[i]), 
        t=max(t,a[i]);
    p2s(1,t);
    cout<<ans<<endl;
    return 0;
 } 

这次改良的版本,用时<0.1秒,完全符合要求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值