二分搜索
对一组有序数组,不断缩小解可能存在的范围,从而求得最优解。
步骤 :1.将数组从小到大排序 2.取中间值与目标比较,不断缩小解所在的区间。
(1)递归
<span style="font-size:18px;">int search(int a[],int left, int right, int goal){
if (left<=right){
int mid = (left+right)/2;
if (a[mid]==goal)
return mid;
else if(a[mid]<goal)
return search(a,mid+1,right,goal);
else if (a[mid]>goal)
return search(a,left,mid-1,goal);
}
else
return -1;
} </span>
(2) 非递归
<span style="font-size:18px;">/* 二分查找
输入:排序好的数组 - sSource[],数组大小 - array_size,查找的值 - key
返回:找到返回相应的位置,否则返回-1
*/
int BinSearch(int sSource[], int array_size, int key)
{
int low = 0, high = array_size - 1, mid;
while (low <= high)
{
mid = (low + high) / 2;
if (sSource[mid] == key)
return mid; //找到则返回相应的位置
if (sSource[mid] > key)
high = mid - 1; //如果比key大,则往低的位置查找
else
low = mid + 1; //如果比key小,则往高的位置查找
}
return -1;
}
</span>
解决的问题: 1)从有序数组中查找某个值
2)假定一个解并判断是否可行//poj1064
3)最大化最小值或最小化最大值//poj2456 解题报告
4)最大化平均值//nyoj 914
4)最大化平均值
题目:有n个物品的重量和价值分别是wi和vi,从中选出k个物品使得单位重量的价值最大。
eg: n=3,k=2,(w,v)={(2,2),(5,3),(2,1)} 输出0.75(选取物品0和2号)。
题解:不能直接把物品按照单位价值排序,否则输出样例为5/7=0.741.
该问题为求 满足使得单位价值不小于x的 最大x。
即vi/wi>=x 变形得vi - wi*x>=0 所以,求满足(vi-wi*x)从大到小排列的前k个的和不小于0.
代码(wa ,没找到错哪里了)
<span style="font-size:18px;">#include<cstdio>
#include<algorithm>
#define INF 0X3f3f3f3f
using namespace std;
int w[10010],v[10010];
double y[10010];//记录v-x*w
int n,k;
bool C(double x){
for (int i=0;i<n;i++){
y[i]=v[i]-w[i]*x;
}
sort (y,y+n);
double sum = 0;
for (int i=0;i<k;i++){
sum+=y[n-1-i];//由大到小累加,求前k个数的和
}
return sum>=0;// 判断是否满足条件(v-x*m)>=0
}
int main(){
scanf("%d %d",&n,&k);
for (int i=0;i<n;i++){
scanf("%d %d",&w[i],&v[i]);
}
double l=0,r=INF;
for (int i=0;i<100;i++){
double mid=(l+r)/2;
if (C(mid)) l=mid;
else r=mid;
}
printf ("%.2lf\n",r);
return 0;
}</span>
ps:注意二分的结束条件
1>.循环次数,1次循环可将范围缩小一半,100次循环可以达到10^30的精度范围。
2>.以题目给的误差范围设为终止条件。(right-left)>EPS EPS=1e-x(根据题目),但是EPS取得太小,容易陷入死循环.
若有两组以上数组,可先组合,再二分