二分算法是一种效率极高的算法(思想),在很多的实例中都得以运用,比如快排和归并排序。本文将对二分算法的一些基础内容以及使用进行讨论。
入门-二分查找
我们先来看一个二分查找的例子。(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秒,完全符合要求。