提示:以下是本篇文章正文内容,下面案例可供参考
一、二分
1.整数型二分
整数型二分主要由两个模板查找
当区间[l,r]被划分成[l,mid]和[mid+1,r]时使用:
模板一
int bsearch(int l,int r)//定义一个区间[l,r]
{
while(l<r)
{
int mid=(l+r)/2;
if(check(mid)) r=mid;//check(mid)为查找满足条件时
else l=mid+1;
}
return l;
}
当区间[l,r]被划分成[mid,r]和[l,mid-1]时使用:
模板二
int bsearch(int l,int r)
{
while(l<r)
{
int mid=(l+r+1)/2;//mid=(l+r+1)/2是为了防止当l=r-1时陷入死循环
if(chack(mid)) l=mid;
else r=mid-1;
}
reuren l;
}
Q:给定一个按照升序排列的长度为n的整数数组及 q 个查询,对于每个查询,返回一个元素k的起始位置和终止位(位置从0开始计数)如果数组中不存在该元素,则返回“-1 -1”。
代码如下:
#include<iostream>
using namespace std;
const int N=100010;
int x[N];
int n,q;
int main()
{
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++) scanf("%d",&x[i]);
while(q--)
{
int k;
scanf("%d",&k);
int l=0,r=n-1;
while(l<r)
{
int mid=(l+r)/2;
if(x[mid]>=k) r=mid;
else l=mid+1;
}
if(x[l]!=k) cout<<"-1 -1"<<endl;
else
{
cout<<l<<' ';
int l=0;r=n-1;
while(l<r)
{
int mid=(l+r+1)/2;
if(x[mid]<=k) l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
}
return 0;
}
运行结果:
2.浮点型二分
浮点型二分相对来说就比较简单,因为每个区间都是精确地,就不用考虑加一或减一的情况。
此外浮点型二分根据经验值,如果题目要求保留四位小数(r-l>1e-6)中1e-6变为1e-7,若保留五位,则变为1e-7,即次方比要求的有效值多2
代码如下:
int main()
{
double x;
cin>>x;
double l=0,r=x;
while(r-l>1e-6)//(1e-6)根据精确值可改变
{
double mid=(l+r)/2;
if(mid*mid>x) r=mid;
else l=mid;
}
printf("%lf",l);
return 0;
}
二、前缀和与差分
前缀和
1.一维前缀和
当原数组为a1 , a2 , a3 . . . an
则该数组前缀和Si = a1 + a2 + a3 + . . . an;
一维前缀和的作用为:快速求原数组中一段数的和
在[ l , r ]中Sr - Sl-1 = ai + ai+1 + . . . + ar
推导:
Sr = a1 + a2 + a3 + . . . + al-1 + al + . . . + ar
Sl-1 = a1 + a2 + a3 + . . . + al-1
则Sr - Sl-1 = al + . . . ar
代码如下:
//一维前缀和
#include<iostream>
using namespace std;
const int N=100010;
int a[N],s[N];
int n,m;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++) s[i]=s[i-1]+a[i];//求和公式
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d",s[r]-s[l-1]);//s[r]-s[l-1]为前缀和公式
}
return 0;
}
运行结果:
2.二维前缀和
作用:快速求某一子矩阵的和
公式:s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1])
代码如下:
//二维前缀和
#include<iostream>
using namespace std;
const int N=1010;
int a[N][N],s[N][N];
int m,n,q;
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
//求前缀和
while(q--)
{
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%d\n",s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);
} //算子矩阵的和
return 0;
}
运行结果:
差分
1.一维差分
概念:
对于数组a1 a2 a3 . . . an ,构造b1 b2 b3 . . . bn ,使得a数组是b数组的前缀和,则b就称为a的差分
构造方法:
令b1 = a1
b2 = a2 - a1
b3 = a3 - a2
.
.
.
bn = an - an-1
则an = b1 + b2 + b3 + . . . + bn
作用:用O( n )的时间由b数组得到a数组
代码如下:
//一维差分
#include<iostream>
using namespace std;
const int N=100010;
int a[N],b[N];
int m,n;
void insert(int l,int r,int c)//插入函数,即在[l,r]中将每一个数加上c
{
b[l]+=c;
b[r+1]-=c;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) insert(i,i,a[i]);
while(m--)
{
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
insert(l,r,c);
}
for(int i=1;i<=n;i++)
b[i]+=b[i-1];//把b数组变成自己的前缀和 求原来数组的值
for(int i=1;i<=n;i++)
printf("%d ",b[i]);
return 0;
}
运行结果:
2.二维差分
在二维差分中,满足原矩阵是差分矩阵的前缀和,给其中一个子矩阵加一个值,可通过构造差分数组来完成,即:
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
代码如下:
//二维差分
#include<iostream>
using namespace std;
const int N=100010;
int a[N][N],b[N][N];
int m,n,q;
void insert(int x1,int y1,int x2,int y2,int c)//插入函数,在子矩阵中求加c后的和
{
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
insert(i,j,i,j,a[i][j]);
while(q--)
{
int x1,y1,x2,y2,c;
cin>>x1>>y1>>x2>>y2>>c;
insert(x1,y1,x2,y2,c);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];//求某一特定子矩阵 的和
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
printf("%d ",b[i][j]);
return 0;
}
运行结果:
三、位运算
四个运算符:
与 & ,只有当x,y都为1的时候运算结果才为1,其余情况均为0。
例:1 & 1 = 1 ,1 & 0 = 0;
或 | ,x,y中只要有一个为1时,结果就是1。
例:1 | 0 = 1,0 | 0 = 0;
非 !,对于!x,如果x是0,!x = 1,如果x是1,则结果为0;
异或 ^,对于x ^ y,x,y相同是0,不同是1。
例:1 ^ 1 = 0,0 ^ 0 = 0,0 ^ 1 = 1;
总结
本文仅介绍了这些算法的基本用法,其中对于二维的前缀和与差分的学习,还是比较费力的,往后还需要进一步的学习与理解。