第二周学习总结


提示:以下是本篇文章正文内容,下面案例可供参考

一、二分

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;
在这里插入图片描述

总结

本文仅介绍了这些算法的基本用法,其中对于二维的前缀和与差分的学习,还是比较费力的,往后还需要进一步的学习与理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值