1.二分查找
①最基础的快乐查找,需要注意的是如果用mid=(left+right)/2,如果right是int上限一半还多,那么可能导致溢出,使用mid=left+(right-left)/2可以代替以避免溢出哦。
②探讨一个新问题:递增序列a是可以重复的,对于想要查找的数字x,求出序列中第一个大于等于x的元素位置l以及第一个大于x的元素位置r,也就是说,x在该序列中的存在区间是[L,R).
1).求l.设当前二分区间为[left,right].如果a[mid]>=x,那么第一个大于等于x的元素一定在mid或者mid左侧。令right=mid.与①的传统做法只有一点点不一样。注意循环条件应该为left<right.二分初始区间为[0,n](假设该元素存在,如果这个元素比所有数都大,就应该返回n)
2).求r是与1类似的。
3).可以考虑用一下c++stl里的lower_bound()和upper_bound().
其中lower_bound(first,last,val)函数在[first,last)进行二分查找返回大于或等于val的第一个元素的位置,如果是数组,返回该位置指针,若没有则返回last的位置。upper_bound()与lower_bound()相似,查找的是第一个大于val的元素的位置。举个栗子0v0:数组a={1,2,4,4,4,5,7,9,9,10,13,17},想查找4第一次出现位置,lower_bound(a,a+n,4)-a,返回值是2.
2.木棒切割问题
以poj1064为例,传送门:http://poj.org/problem?id=1064
题意:给出n条木棒长度,想切割k条相同长度的木棒,求这个长度的max.
思路:如果长度L越大,那么可以得到的k就越小。题目已经给出了k,来二分L,根据当前的L得出的k'与k的大小关系来判断。比如,如果当前的k'大于k,说明这个L取小了。
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxv=1e4+4;
const double eps=1e-6;
double c[maxv];
int n,k;
int num(double x)
{
int co=0;
for(int i=0;i<n;i++)
{
co+=(int)(c[i]/x);
}
return co;
}
int main()
{
cin>>n>>k;
double left=0,right=0,mid;
for(int i=0;i<n;i++)
{
cin>>c[i];
right=max(right,c[i]);
}
while(right-left>=eps)
{
mid=(left+right)/2;
if(num(mid)>=k)
left=mid;
else
right=mid;
}
if((int)(right*1000)%10>=5)
right-=0.005;
printf("%.2lf\n",right);
return 0;
}
3.快速幂
①比如,想要算a^b%m,当abm都很大的时候,显然暴力循环是不行的。
而聪明机智的快速幂基于二分的思想:
1)如果b是奇数,那么a^b=a^(b-1)*a
2)如果b是偶数,那么a^b=a^(b/2)*a^(b/2)
可以看出,奇数偶数情况可以相互转换,那么最后必然可以把b变成0,那肯定就是1,这显然是递归的思想,代码如下:
typedef long long ll;
ll binary_pow(ll a,ll b,ll m)
{
if(b==0)
return 1;
if(b%2!=0)
return a*binary_pow(a,b-1,m)%m;
else
{
ll mul=binary_pow(a,b/2,m);
return mul*mul%m;
}
}
ps:b%2==0时直接两个函数相乘的写法会调用两次函数,导致复杂度上升成O(b),而使用中间值mul,是O(logb)
②迭代写法。
通过二进制来实现。把b写成二进制,那么就是若干个二次幂的和。那么a^b可以表示成若干个a的2次幂相乘,且前一项总是等于后一项的平方。
1)初始化ans=1
2)判断b的二进制末尾是不是1,是1那就是奇数,令ans*a
3)令a平方,将b/2(右移一位)
4)返回2),b=0退出
ll quick_pow(ll a,ll b,ll m)
{
ll ans=1;
while(b>0)
{
if(b&1)
{
ans=ans*a%m;
}
a=a*a%m;
b>>=1;
}
return ans;
}
③矩阵快速幂。
以poj3070为例,传送门:http://poj.org/problem?id=3070 玩一下矩阵快速幂。
题意:求斐波那契数列第n项对10000取余的结果,n<=10^16
思路:参考了https://blog.csdn.net/ccf15068475758/article/details/52846726题解,使用矩阵乘法求。
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=1e4;
typedef long long ll;
struct node
{
int v[3][3];
int m,l;
}a[10];
/*ll binary_pow(ll a,ll b,ll m)
{
if(b==0)
return 1;
if(b%2!=0)
return a*binary_pow(a,b-1,m)%m;
else
{
ll mul=binary_pow(a,b/2,m);
return mul*mul%m;
}
}
ll quick_pow(ll a,ll b,ll m)
{
ll ans=1;
while(b>0)
{
if(b&1)
{
ans=ans*a%m;
}
a=a*a%m;
b>>=1;
}
return ans;
}*/
int n;
node get_mul(node a,node b)
{
node c;
c.m=a.m;
c.l=b.l;
for(int i=1;i<=c.m;i++)
{
for(int j=1;j<=c.l;j++)
{
c.v[i][j]=0;
for(int k=1;k<=a.l;k++)
{
c.v[i][j]=(c.v[i][j]+a.v[i][k]*b.v[k][j])%mod;
}
}
}
return c;
}
int main()
{
while(scanf("%d",&n))
{
if(n==-1)
return 0;
if(n==0)
{
printf("0\n");
continue;
}
a[0].m=a[0].l=2;
a[0].v[1][1]=a[0].v[1][2]=a[0].v[2][1]=1;
a[0].v[2][2]=0;
a[1].m=a[1].l=2;
a[1].v[1][1]=a[1].v[2][2]=1;
a[1].v[1][2]=a[1].v[2][1]=0;
a[2].m=2,a[2].l=1;
a[2].v[1][1]=1;
a[2].v[2][1]=0;
n--;
while(n)
{
if(n&1)
a[1]=get_mul(a[0],a[1]);
a[0]=get_mul(a[0],a[0]);
n>>=1;
}
a[1]=get_mul(a[1],a[2]);
printf("%d\n",a[1].v[1][1]);
}
return 0;
}