数据结构总结之二分

来源:互联网 发布:淘宝达人怎么获得佣金 编辑:程序博客网 时间:2024/04/30 01:14

1.找惟一的一个与x相等的元素的位置

int search(int *arr, int n, int key){    int left = 0, right = n-1;    while(left<=right) {        int mid = left + ((right - left) >> 1);//防止溢出        if (arr[mid] == key)            return mid;         else if(arr[mid] > key)             right = mid - 1;        else             left = mid + 1;    }    return -1;}

2.找第一个与x相等的元素位置

int searchFirstEqual(int *arr, int n, int key){    int left = 0, right = n-1;    while(left < right)    {        int mid = (left+right)/2;        if(arr[mid] > key)            right = mid - 1;        else if(arr[mid] < key)             left = mid + 1;        else            right=mid;    }    if(arr[left] == key)             return left;    return -1;}

3.找到最后一个与x相等的元素位置

int searchLastEqual(int *arr, int n, int key){    int left = 0, right = n-1;    while(left<right-1) {        int mid = (left+right)/2;        if(arr[mid] > key)             right = mid - 1;        else if(arr[mid] < key)             left = mid + 1;         else            left=mid;    }    if( arr[left]<=key && arr[right] == key)         return right;    if( arr[left] == key && arr[right] > key)        return left;    return -1;}

4.找到第一个大于等于x的元素位置

int searchFirstEqualOrLarger(int *arr, int n, int key){    int left=0, right=n-1;    while(left<=right)     {        int mid = (left+right)/2;        if(arr[mid] >= key)             right = mid-1;        else if (arr[mid] < key)             left = mid+1;    }    return left;}

5.找到第一个大于x的元素位置

int searchFirstLarger(int *arr, int n, int key){    int left=0, right=n-1;    while(left<=right)    {        int mid = (left+right)/2;        if(arr[mid] > key)             right = mid-1;        else if (arr[mid] <= key)             left = mid+1;    }    return left;}

6.找到最后一个小于等于x的元素位置

int searchLastEqualOrSmaller(int *arr, int n, int key){    int left=0, right=n-1;    while(left<=right)     {        int m = (left+right)/2;        if(arr[m] > key)              right = m-1;        else if (arr[m] <= key)              left = m+1;    }    return right;}

7.找最后一个小于x的元素位置

int searchLastSmaller(int *arr, int n, int key){    int left=0, right=n-1;    while(left<=right) {        int mid = (left+right)/2;        if(arr[mid] >= key)              right = mid-1;        else if (arr[mid] < key)              left = mid+1;    }    return right;}

8.要用二分,首先要构造一个单调性的数组,比如这题(当然也有其他方法)
uva10706
构造的s[i]数组代表112123123412345123456123……i的i所在的位置。

#include<iostream>#include<string>#include <stdio.h>#include <stack>using namespace std;long long s[100000];long long n,i;int f(int x){    if(x==1) return 1;    long long cnt=s[x-1]-s[x-2];    while(x)    {        x/=10;        cnt++;    }    return cnt;}int Bsearch(int x){    int h=i;    int l=1;    while(h>=l)    {        int mid=(h+l)/2;        if(s[mid]>=x) h=mid-1;        else l=mid+1;    }    return l;}int out_result(int pos){    int cnt=n-s[pos-1];    for(int i=1;i<=pos;i++)    {        int tmp=i;        stack<int> s;//这里用栈得到了一个数的第一个第二个……第i个数字        while(tmp)        {            s.push(tmp%10);            tmp/=10;        }        while(!s.empty())        {            int ans=s.top();s.pop();            if(--cnt==0) return ans;        }    }}int main(){    for(i=1;s[i-1]<= 2147483647;i++)    {         s[i]=s[i-1]+f(i);    }    int t;cin>>t;    while(t--)    {        scanf("%d",&n);        int pos=Bsearch(n);        printf("%d\n",out_result(pos));    }    return 0;}

9.我们在构造数组的时候,要注意数组不能太大,怎么解决这个问题呢?——移项。例如:hdu4282

#include <iostream>#include <cstdio>#include <cstring>using namespace std;const __int64 maxn=1<<16;__int64 k;__int64 a[maxn][32];const __int64 MAX=2147483648;//1<<31必须这样写……void init(){    memset(a,0,sizeof(a));    for(int i=1;i<maxn;i++)    a[i][1]=i;    for(int i=1;i<maxn;i++)        for(int j=2;j<32;j++)        {             a[i][j]=a[i][j-1]*i;             if(a[i][j]>=MAX)             {                  a[i][j]=0;                  break;             }        }}bool f(__int64 sum,__int64 e,__int64 x){    __int64 l,h,mid;    l=x+1;    h=maxn;    while(l<=h)    {        mid=(h+l)/2;        if(a[mid][e]==0)        {            h=mid-1;            continue;        }        __int64 tmp=a[mid][e]+e*mid*x;        if(tmp==sum)            return true;        else if(tmp>sum)            h=mid-1;        else            l=mid+1;    }    return false;}int main(){    init();//设置乘方表,大于等于2的31次方的都为0    while(~scanf("%I64d",&k) && k)    {        __int64 ans=0;        __int64 sum=0;        __int64 e;        for(__int64 x=1;x<maxn;x++)            for(e=2;e<32;e++)             {                if(a[x][e]==0) break;                sum=k-a[x][e];                if(sum<=0) break;                if(f(sum,e,x))                     ans++;            }        printf("%I64d\n",ans);    }    return 0;}

一开始,我构造的数组是x^z+y^z+x*y*z.也就是用三层循环构造数组,结果爆内存。我们可以这样:输入一个k,循环z和x,这样对于每一个z和x,构造的数组就是y^z+x*y*z,然后找与k-x^z相等的y,找到了答案就+1.
10.如果题目让求满足某一条件的最小值,也是用二分法的,比如uva11413

#include <iostream>#include <cstdio>#include <cstring>using namespace std;int v[1001];int n,m;int container_num(int size){    int sum=0;    int count=0;    for(int i=0;i<n;i++)    {        if(sum+v[i]>size)        {            sum=v[i];//当“够了”sum立即变成下一个的sum,然后次数+1            count++;        }        else            sum+=v[i];    }    return count+1;}int main(){    while(scanf("%d%d",&n,&m)!=EOF)    {        int max=0;        int sum=0;        for(int i=0;i<n;i++)        {             scanf("%d",&v[i]);             sum+=v[i];             if(max<v[i]) max=v[i];        }           int l=max;           int h=sum;           int mid;        while(l<h)        {            mid=(l+h)/2;            if(container_num(mid)<=m)                h=mid;//仔细想想为什么不是mid-1?            else                l=mid+1;        };        printf("%d\n",l);    }}

11.还要注意的有二分法只能对于int型,二分上限下限也要找对。如:uva11516

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;int loc[100005];int n,m;bool f(int mid){    int start=loc[0];    int need=1;    for(int i=0; i<m; i++)        if(loc[i]>start+mid)        {            need++;            start=loc[i];        }    if(need>n) return false;    else return true;}int main(){    int t;    cin>>t;    while(t--)    {        scanf("%d%d",&n,&m);        for(int i=0; i<m; i++)            scanf("%d",&loc[i]);        sort(loc,loc+m);        int hi,lo,mid;        hi=(loc[m-1]-loc[0])*2;        lo=0;        while(hi>lo)        {            mid=(hi+lo)/2;            if(!f(mid))//因为位置都是int,所以直径也是int型,故可以二分                lo=mid+1;            else                hi=mid;        }        printf("%.1f\n",lo/2.0);    }    return 0;}
原创粉丝点击