一.cpu的分支预测
(学了个怪东西)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
// likely 代表 x 经常成立
// unlikely 代表 x 不经常成立
在cpu执行有关选择结构的流水线操作中可以通过这段代码以加速程序运行;
其它内置的builtin函数:
二.前缀和与差分
1.基础概念
<1>.前缀和:指某序列的前n项和;
<2>.差分:某序列前后两项的差;
<3>转换关系:差分序列(前缀和操作)原序列;
原序列(前缀和操作)前缀和序列;
<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个数实现加数的操作