(noi.openjudge.cn) 1.11编程基础之二分查找T01、02、04

T01 查找最接近的元素

描述

在一个非降序列中,查找与给定值最接近的元素。

输入

第一行包含一个整数n,为非降序列长度。1 <= n <= 100000。
第二行包含n个整数,为非降序列各元素。所有元素的大小均在0-1,000,000,000之间。
第三行包含一个整数m,为要询问的给定值个数。1 <= m <= 10000。
接下来m行,每行一个整数,为要询问最接近元素的给定值。所有给定值的大小均在0-1,000,000,000之间。

输出

m行,每行一个整数,为最接近相应给定值的元素值,保持输入顺序。若有多个值满足条件,输出最小的一个。

样例输入
3
2 5 8
2
10
5
样例输出
8
5
样例
 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 using namespace std;
 5 int n,a[100001],m,x;
 6 int main()
 7 {
 8     scanf("%d",&n);
 9     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
10     scanf("%d",&m);
11     for(int i=1;i<=m;i++)
12     {
13         scanf("%d",&x);
14         int l=1,r=n,mid,cha=1000000001,ans;//cha为当前最小的差的绝对值,ans为得出cha的序列元素 
15         while(l<=r)
16         {
17             mid=(l+r)/2;
18             int h=abs(a[mid]-x);//x与当前查找到的元素的差的绝对值 
19             if(!h) //h=0,说明原序列中有x 
20             {
21                 ans=a[mid];
22                 break;
23             }
24             else if(a[mid]<x) l=mid+1;//中点比x小,往右找 
25             else r=mid-1; //中点比x大,往左找 
26             if(h<cha) ans=a[mid],cha=h;
27             else if(h==cha&&ans>a[mid]) ans=a[mid];//若有多个满足条件,输出最小的一个 
28         }
29         printf("%d
",ans);
30     }
31 }
View Code

交了5遍过的,2个问题:

1、序列元素与差值混了

2、错误的认为二分出来的同等差值的第一个一定是最小的一个

T02 二分法求函数的零点

描述

有函数:

f(x) = x5 - 15 * x4+ 85 * x3- 225 * x2+ 274 * x - 121

已知 f(1.5) > 0 , f(2.4) < 0 且方程 f(x) = 0 在区间 [1.5,2.4] 有且只有一个根,请用二分法求出该根。

输入

无。

输出

该方程在区间[1.5,2.4]中的根。要求四舍五入到小数点后6位。

样例输入
无
样例输出
不提供
样例

因为f(1.5) > 0 , f(2.4) < 0且方程 f(x) = 0 在区间 [1.5,2.4] 有且只有一个根,所以函数在区间[1.5,2.4]内单调递减

先将区间中点带入,若小于0,则此区间中点位于零点右侧,所以要向左找;若大于0,则此区间中点为于零点左侧,所以要向右找

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 double l=1.5,r=2.4,,xx;
 5 double work(double x)
 6 {
 7   return  x*x*x*x*x-15*x*x*x*x+85*x*x*x-225*x*x+274*x-121;
 8 }
 9 int main()
10 {
11     while(r-l>=0.000001)
12     {
13         xx=(l+r)/2;
14         if(work(xx)<=0) r=xx;
15         else l=xx; 
16     }
17     printf("%.6lf",xx);
18 } 
View Code

做的时候跟曾经做过的一元三次方程求解那道题混了,一元三次方程求解2001年NOIP全国联赛提高组

T04 网线主管

描述

仙境的居民们决定举办一场程序设计区域赛。裁判委员会完全由自愿组成,他们承诺要组织一次史上最公正的比赛。他们决定将选手的电脑用星形拓扑结构连接在一起,即将它们全部连到一个单一的中心服务器。为了组织这个完全公正的比赛,裁判委员会主席提出要将所有选手的电脑等距离地围绕在服务器周围放置。

为购买网线,裁判委员会联系了当地的一个网络解决方案提供商,要求能够提供一定数量的等长网线。裁判委员会希望网线越长越好,这样选手们之间的距离可以尽可能远一些。

该公司的网线主管承接了这个任务。他知道库存中每条网线的长度(精确到厘米),并且只要告诉他所需的网线长度(精确到厘米),他都能够完成对网线的切割工作。但是,这次,所需的网线长度并不知道,这让网线主管不知所措。

你需要编写一个程序,帮助网线主管确定一个最长的网线长度,并且按此长度对库存中的网线进行切割,能够得到指定数量的网线。

输入

第一行包含两个整数N和K,以单个空格隔开。N(1 <= N <= 10000)是库存中的网线数,K(1 <= K <= 10000)是需要的网线数量。
接下来N行,每行一个数,为库存中每条网线的长度(单位:米)。所有网线的长度至少1m,至多100km。输入中的所有长度都精确到厘米,即保留到小数点后两位。

输出

网线主管能够从库存的网线中切出指定数量的网线的最长长度(单位:米)。必须精确到厘米,即保留到小数点后两位。

若无法得到长度至少为1cm的指定数量的网线,则必须输出“0.00”(不包含引号)。

样例输入
4 11
8.02
7.43
4.57
5.39
样例输出
2.00
样例

本题一定要特别注意精度问题,在输入时,把每个输入的数*100之后在存储,输出时/100输出,否则只能得8分

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int n,m;
 5 int a[10001];
 6 double ans;
 7 double x;
 8 bool ok(int k)
 9 {
10     int s=0;
11     for(int i=1;i<=n;i++)
12      s+=a[i]/k;
13     if(s<m) return false;
14     else    return true;
15 }
16 int main()
17 {
18     scanf("%d%d",&n,&m);
19     for(int i=1;i<=n;i++) 
20     {
21         scanf("%lf",&x);
22         a[i]=x*100;
23     }
24     int l=1,r=10000005;//最大值100km,即10000000cm,可以稍微大一点。r还可以赋值为只看输入数据中的最大值,这样只需要在输入时记录一个max,把max复制给r
25     while(l<=r)
26     {
27         int mid=(l+r)/2;
28         if(ok(mid)) ans=mid,l=mid+1;
29         else r=mid-1;
30     }
31     ans=ans/100;
32     if(ans<0.01) printf("0.00");
33     else printf("%.2lf",ans);
34 }
View Code

交了28遍才过,总之就是没有发现2个问题:

1、精度问题,没有把输入数据*100在计算,直接按double算的

2、赋值r的时候,一直想着r的最大值是所有网线长度总和再除以需要的网线数量,忽略了有的网线可以不用,这就拉低了最大值,实际上r的最大值应为所有网线长度中的最大值

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6131351.html