您的位置:首页 > 理论基础 > 数据结构算法

数据结构总结之二分

2017-12-16 16:56 323 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: