[蓝桥杯的一些基础算法的板子]y总课程中归纳出来的

1.归并排序

int q[maxn], w[maxn];//q为原数组 w为临时数组

void merge_sort(int l, int r) {//归并的左右界限
    if (l >= r) return;//如果只剩一个元素 返回
    int mid = l + r >> 1;//否则寻找中点
    merge_sort(l, mid);//递归左半部分
    merge_sort(mid + 1, r);//递归右半部分
    //归并过程
    int i = l, j = mid + 1, k = 0;//左右两个数组 i指向左边的数组的第一个 j指向右边数组的第一个 k为了方便从0开始
    while (i <= mid && j <= r) {//当i j均未结束的时候
        if (q[i] < q[j]) w[k++] = q[i++];//将小的归到w数组中
        else w[k++] = q[j++];
    }
    while (i <= mid) w[k++] = q[i++];//如果i未结束 继续加入 可以简单证明一定是最大的
    while (j <= r)w[k++] = q[j++];//同理
    for (i = l, j = 0; i <= r; i++, j++) {//i是这一段数组(无序) j指向我们刚刚操作完毕的w数组 将w数组放入到q数组当中 保证q数组的这一部分变得有序 第二个条件可以改成j<k 一个道理
        q[i] = w[j];
    }
}

2.前缀和和差分

“下次你路过,人间已无我,但我的国家,依然是五岳向上,一切江河依然是滚滚向东,民族的意志依然向前,向着热腾腾的太阳,跟你一样。” —-余光中

十分基础的知识了 一直没有整理一下
前缀和和差分可以说是互逆的算法
前缀和用来解决一个数组多次查询某区间值
差分用来解决多次修改后查数组
一个偏向于差一个偏向于改 两者兼顾要用树状数组
应用这两种算法时 一般数组从1开始用

一位前缀和和二维前缀和

原数组为a[i]
建立数组b[i],使得b[i]=a[1]+a[2]+…+a[i]
求和的时候用b[r]-b[l-1]快速求和

    for (int i = 1; i <= n;i++){
        cin >> a[i];
    }
    for (int i = 1; i <= n;i++){
        b[i] = b[i - 1] + a[i];
    }

关于二维前缀和要使用容斥原理 如图所示 要求绿色格子的值 先要把红色部分的值加上 再加上橙色部分的值 此时发现加多了一块 图三标识为紫色 再减去紫色 同时加上绿色格子本身的值即可 查询的时候同理 看代码

    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            b[i][j] = b[i][j - 1] + b[i - 1][j] - b[i - 1][j - 1] + a[i][j];
        }
    }
    while (q--) //查询
    {
        int x1, y1, x2, y2; //这里假定x1 y1为左上角 x2 y2为右下角的值
        cin >> x1 >> y1 >> x2 >> y2;
        int ans = b[x2][y2] - b[x1 - 1][y2] - b[x2][y1 - 1] + b[x1 - 1][y1 - 1];
        cout << ans << endl;
    }

差分
原数组是a[]
构造一个数组b[] 使得a[i]=b[1]+b[2]+…+b[i] 具体如何构造我们不关心 假设一开始a中所有元素都是0 只需要将a每个位置加上a[i]即可
建议修改写成一个函数
如果修改a[l]-a[r] 每个加上1 修改b[r+1]-=c b[l]+=c即可

void insert(int l,int r,int c){
    b[l] += c;
    b[r + 1] -= c;
}

int main(){
    cin >> n >> m;
    for (int i = 1; i <= n;i++){
        cin >> a[i];
        insert(i, i, a[i]);//插入原数组
    }
    for (int i = 0; i < m;i++){//修改
        int l, r, c;
        cin >> l >> r >> c;
        insert(l, r, c);
    }
    for (int i = 1; i <= n;i++){
        a[i] = a[i - 1] + b[i];
        cout << a[i] << ' ';
    }
    return 0;
}

二维差分
注意二维差分的插入操作的写法 一个格子加了c 对应的效果是他的右下方全部加了c

void insert(int x1,int y1,int x2,int y2,int c){
    b[x1][y1] += c;//左下方全部加上
    b[x1][y2 + 1] -= c;//下面减去
    b[x2+1][y1] -= c;//左边减去
    b[x2 + 1][y2 + 1] += c;//加回重复减去的
}

int main(){
    ios::sync_with_stdio(0);
    cin >> n >> m >> q;
    for (int i = 1; i <= n;i++){
        for (int j = 1; j <= m;j++){
            cin >> a[i][j];
            insert(i, j, i, j, a[i][j]);
        }
    }
    int x1, x2, y1, y2, c;
    for (int i = 0; i < q;i++){
        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++){
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];//求前缀和和前面一样
            cout << a[i][j] << ' ';
        }
        cout << endl;
    }
}

3.二分查找

int bs(int l, int r)
{ //分成[l,mid] [mid+1,r] r=mid/l=mid+1
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    return l;
}

int bs(int l, int r)
{ //分成[l,mid-1] [mid,r] r=mid+1/l=mid
    while (l < r)
    {
        int mid = l + r + 1 >> 1; //注意这里的加1
        if (check(mid))
            l = mid;
        else
            r = mid - 1;
    }
    return l;
}

4.素数筛

在O(n)的时间复杂度中求出1-n中的所有素数
素数(质数):因子(在大于1的自然数中)只有1和本身的数

普通筛法中,一个和数可能被多个素数筛掉 比如6 被2筛一次 又被3筛一次 浪费了时间复杂度 线性筛法中将合数表示成最小素数*一个数的形式
枚举到i 设p是i的最小质因子 p1<p2<…<p(全是质数) 将p1*i,p2*i,…p*i标记为合数 这样能保证一个数字只被筛掉一次

int primes[N], cnt; //primes存素数 cnt计数
bool st[N];			//st记录该数是否是素数

void get_primes(int n)
{
	for (int i = 2; i <= n; i++) //从2开始
	{
		if (!st[i])
			primes[cnt++] = i; //如果该数是素数 那么将其加入primes数组
		for (int j = 0; primes[j] * i <= n; j++)//之后筛掉倍数
		{
			st[primes[j] * i] = true;//标记
			if (i % primes[j] == 0)//已经筛到p了
				break;
		}
	}
}

You may also like...

发表评论

电子邮件地址不会被公开。 必填项已用*标注