ACM零起点2017-7-27(查找算法之------尺取法、二分法、三分法)

来源:互联网 发布:淘宝网外国小女孩模特 编辑:程序博客网 时间:2024/06/05 09:13

第一波:尺取法

应用于有这么一类问题:需要在给的一组数据中找到不大于某一个上限的“最优连续子序列


例题 Subsequence POJ - 3061

题目来源:https://vjudge.net/problem/POJ-3061


A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given. Write a program to find the minimal length of the subsequence of consecutive elements of the sequence, the sum of which is greater than or equal to S.
Input
The first line is the number of test cases. For each test case the program has to read the numbers N and S, separated by an interval, from the first line. The numbers of the sequence are given in the second line of the test case, separated by intervals. The input will finish with the end of file.
Output
For each the case the program has to print the result on separate line of the output file.if no answer, print 0.
Sample Input
210 155 1 3 5 10 7 4 9 2 85 111 2 3 4 5
Sample Output
23


本题应用尺取法思路:

分析:首先,序列都是正数,如果一个区间其和大于等于S了,那么不需要在向后推进右端点了,因为其和也肯定大于等于S但长度更长,所以,当区间和小于S时右端点向右移动,和大于等于S时,左端点向右移动以进一步找到最短的区间,如果右端点移动到区间末尾其和还不大于等于S,结束区间的枚举。

针对样例1,尺取法的具体过程如下:


AC代码:


 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233
#include<cstdio>#include<algorithm>using namespace std;const int maxn=100005;long long a[maxn];int main(){    int t;    scanf("%d",&t);    while(t--)    {        int n;        long long sum=0,s;        int ans=maxn;        scanf("%d%I64d",&n,&s);        for(int i=0;i<n;i++)            scanf("%I64d",&a[i]);        int st=0,en=0;        while(1)        {            while(en<n && sum<s)sum+=a[en++];            if(sum<s)break;            ans=min(ans,en-st);            sum-=a[st++];        }        if(ans==maxn)ans=0;        printf("%d\n",ans);    }    return 0;}


第二波:二分法

下面包含了STL中的binary_search(),upper_bound(),lower_bound()


 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
#include<iostream>#include<algorithm>using namespace std;//二分查找,区间左闭右开,针对有序列int bin_search(int a[],int left,int right,int v){    int mid;    while(left<right)    {        mid=left+(right-left)/2;        if(v==a[mid])return mid;        else if(v<a[mid])right=mid;        else left=mid+1;    }    return -1;}//利用上面的二分查找算法,寻找下界int low_bound(int a[],int left,int right,int v){    int mid;    while(left<right)    {        mid=left+(right-left)/2;        if(v<=a[mid])right=mid;        else left=mid+1;    }    return left;}//利用上面的二分查找算法,寻找上界int up_bound(int a[],int left,int right,int v){    int mid;    while(left<right)    {        mid=left+(right-left)/2;        if(v>=a[mid])left=mid+1;        else right=mid;    }    return left;}int main(){    int a[5];    int v;//要查找的数    for(int i=0;i<5;i++)        a[i]=i+10;    bool success=binary_search(a+0,a+5,12);//库函数,查找是否成功,返回bool值    int location=bin_search(a,0,5,12);//返回查找到元素的位置,如果没有查到,则返回-1    cout<<"12 "<<(success?"found":"not found");    if(success)//也可以写成:if(location>=0)        cout<<" ,location:"<<location<<endl;    cout<<"lower_bound:"<<*lower_bound(a,a+5,12)<<endl;//针对非递减序列,返回第一个大于等于某个数的值    cout<<"upper_bound:"<<*upper_bound(a,a+5,12)<<endl;//针对非递减序列,返回第一个大于某个数的值    cout<<"low_bound:"<<low_bound(a,0,5,12)<<endl;//返回的是对应数组中的下标    cout<<"up_bound:"<<up_bound(a,0,5,12)<<endl;//返回的是对应数组中的下标    return 0;}




第三波:三分法


三分法应用于单峰凸函数,下面举一个例子(借用:http://blog.csdn.net/pi9nc/article/details/9666627)




如图所示,已知左右端点L、R,要求找到白点的位置。

思路:通过不断缩小 [L,R] 的范围,无限逼近白点。

做法:先取 [L,R] 的中点 mid,再取 [mid,R] 的中点 mmid,通过比较 f(mid) 与 f(mmid) 的大小来缩小范围。

           当最后 L=R-1 时,再比较下这两个点的值,我们就找到了答案。

1、当 f(mid) > f(mmid) 的时候,我们可以断定 mmid 一定在白点的右边。

反证法:假设 mmid 在白点的左边,则 mid 也一定在白点的左边,又由 f(mid) > f(mmid) 可推出 mmid < mid,与已知矛盾,故假设不成立。

所以,此时可以将 R = mmid 来缩小范围。

2、当 f(mid) < f(mmid) 的时候,我们可以断定 mid 一定在白点的左边。

反证法:假设 mid 在白点的右边,则 mmid 也一定在白点的右边,又由 f(mid) < f(mmid) 可推出 mid > mmid,与已知矛盾,故假设不成立。

同理,此时可以将 L = mid 来缩小范围。


伪代码实现:


  1. int SanFen(int l,int r) //找凸点  
  2. {  
  3.     while(l < r-1)  
  4.     {  
  5.         int mid  = (l+r)/2;  
  6.         int mmid = (mid+r)/2;  
  7.         if( f(mid) > f(mmid) )  
  8.             r = mmid;  
  9.         else  
  10.             l = mid;  
  11.     }  
  12.     return f(l) > f(r) ? l : r;  
  13. }  



【题目实战】


开始每个人都在一条数轴上的某个位置上,位置大于等于1,都是整数,每个人有个最大移动速度,在数轴上某个点集合,可以花费最短的时间,求出这个最短的时间。



AC代码:


1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142
#include<cstdio>#include<algorithm>#include<cmath>#define INF 0x3f3f3f3fusing namespace std;const double eps=0.000001;const int maxn=60010;double a[maxn],b[maxn];int n;double check(double x){    double ans=0;    for(int i=0;i<n;i++)        ans=max(ans,fabs(x-a[i])/b[i]);    return ans;}int main(){    double mmin=1.0*INF,mmax=-1.0*INF;    scanf("%d",&n);    for(int i=0;i<n;i++)    {        scanf("%lf",&a[i]);        mmin=min(mmin,a[i]);        mmax=max(mmax,a[i]);    }    for(int i=0;i<n;i++)        scanf("%lf",&b[i]);    double l=mmin,r=mmax;    while(l+eps<r)    {        double mid=(l+r)/2;        double mmid=(mid+r)/2;        if(check(mid)>check(mmid))l=mid;        else r=mmid;    }    printf("%.12lf\n",check(r));    return 0;}


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 宝宝出生17天睡眠不安稳怎么办 月子里宝宝睡觉老是睡不安稳怎么办 孕妇晚期咳嗽鼻涕黄咽喉痛怎么办 狗狗流脓鼻涕拉稀没食欲怎么办 孕妇眼睛充血很快就有眼屎怎么办 婴儿的眼睛流泪生眼屎怎么办呀 刚出生的婴儿眼睛有眼屎怎么办 25天的婴儿鼻子有鼻屎不通怎么办 一个月的宝宝好多鼻屎怎么办 隆鼻取线的时候好多鼻屎怎么办 小孩流浓鼻涕怎么办最简单方法 小孩流黄鼻涕怎么办最简单方法 一岁八个月宝宝鼻涕和痰多怎么办 空调铜管过不了预埋管的弯头怎么办 如果朝鲜和韩国打起来中国怎么办 寄信时不知道对方的邮编怎么办 地下钱庄转账后银行户被冻结怎么办 老师遇到素质极差的垃圾学生怎么办 验证码忘了手机号也换了怎么办 手机上的验证码忘了怎么办 进入医联网的验证码忘了怎么办 育碧换电脑了无法同步云存档怎么办 刺客信条起源育碧需要激活码怎么办 电脑连不上网怎么办wifi可以用 电脑登录账号密码错误锁定了怎么办 白色T恤衫上沾上黑色的黄油怎么办 家教遇到成绩好的学生该怎么办 跟越南人离婚孩子中国户口9怎么办 老婆是个越南人至今没户口怎么办 等离子屏z板链接处排线打火怎么办 等离子自动调焊的成形不好怎么办 村里内村道路中间被抢占了怎么办 华为换电池之后卡没反应怎么办 汽车钥匙换电池后没反应怎么办 汽车解锁换电池后没反应怎么办 包裹显示待收件人向海关申报怎么办 在越南签证被公安扣了怎么办 酷派手机收不到验证码怎么办 苹果想把图片上的字盖上怎么办 婴儿自己把眼珠子抠红了怎么办 如果美陆战队员进入台湾那怎么办?