11.19

一.cpu的分支预测

(学了个怪东西)

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

// likely 代表 x 经常成立
// unlikely 代表 x 不经常成立

 在cpu执行有关选择结构的流水线操作中可以通过这段代码以加速程序运行;

其它内置的builtin函数:

__builtin_ffs(x) 返回x中最后一个为1的位是从后向前的第几位
__builtin_popcount(x) x中1的个数
__builtin_ctz(x) x末尾0的个数。x=0时结果未定义
__builtin_clz(x) x前导0的个数。x=0时结果未定义
__builtin_prefetch (const void *addr, ...) 对数据手工预取的方法
__builtin_types_compatible_p(type1, type2) 判断type1和type2是否是相同的数据类型
__builtin_expect (long exp, long c) 用来引导gcc进行条件分支预测
__builtin_constant_p (exp) 判断exp是否在编译时就可以确定其为常量
__builtin_parity(x) x中1的奇偶性
__builtin_return_address(n) 当前函数的第n级调用者的地址

二.前缀和与差分

1.基础概念

<1>.前缀和:指某序列的前n项和;

<2>.差分:某序列前后两项的差;

<3>转换关系:差分序列\rightarrow(前缀和操作)\rightarrow原序列;

                        原序列\rightarrow(前缀和操作)\rightarrow前缀和序列;

<4>原序列第一项默认为0;即arr[0]=0;

2.前缀和的作用

示例题目:

若通过暴力解法来遍历求和,程序在运行较大数据时就可能超时;

代码如下:

#include <stdio.h>

int main(){
    int n;
    scanf("%d", &n);
    int a[100000] = {0};
    for(int i = 0; i < n; i++){
        scanf("%d", &a[i]);
    }
    int m;
    scanf("%d", &m);
    int b[10000] = {0};
    int count = 0;
    while(m--){
        int l, r;
        scanf("%d%d", &l, &r);
        for(int i = l - 1; i < r; i++){
            b[count] += a[i];
        }
        count++;
    }
    for(int i = 0; i < count; i++){
        printf("%d\n", b[i]);
    }
    return 0;
}

此时我们可以通过前缀和序列的特点来优化此程序以减少运行时间;

具体操作:

        1.重新定义一个数组求出原序列至第n项的前缀和;

        2.从前缀和数组中进行查询操作;

代码如下:

#include <stdio.h>

int a[1000000] = {0};
int b[10000] = {0};//前缀和数组

int main(){
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++){
        scanf("%d", &a[i]);
    }
    for(int i = 0; i < n; i++){
        b[i + 1] = b[i] + a[i];
    }
    int m;
    scanf("%d", &m);
    //执行访问操作
    while(m--){
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", b[r] - b[l - 1]);
    }
    return 0;
}

原理如下:

b[r] = a[1] + a[2] + ....... + a[r];

b[l - 1] = a[1] + a[2] +......+a[l - 1];

b[r] - b[l - 1] = a[l] + a[l + 1] +......+ a[r];

综上,在预处理后,仅需经过几次运算即可求出第l个数至第r个数的和,极大减少了程序的时间复杂度

(以上称之为一维前缀和)

3.差分的作用

在了解前缀和后,我们可以通过另一道题进一步了解差分的作用:

 


代码如下:

#include <stdio.h>

int a[10000000] = {0};
int b[10000000] = {0};
int c[10000000] = {0};



int main(){
    int n, p;
    scanf("%d%d", &n, &p);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
    }
    for(int i = 1; i <= n; i++){
        b[i] = a[i] - a[i - 1];
    }
    while(p--){
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        b[x] += z;
        b[y + 1] -=z;
    }
    for(int i = 0; i < n; i++){
        c[i] = c[i - 1] + b[i];
    }
    int min = c[1];
    for(int i = 2; i <= n; i++){
        if(min > c[i] && c[i] != 0){
            min = c[i];
        }
    }
    printf("%d ", min);
}

原理如下:

a[0] = 0;

b[1] = a[1]  - a[0];

b[2] = a[2] - a[1];

........

b[n] = a[n] - a[n - 1];

预处理后数组b即为a[n]的差分数组,而在第x个位置+z和第y个位置-z再进行前缀和操作即能实现对第x至第y个数实现加数的操作

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值