0
点赞
收藏
分享

微信扫一扫

蓝桥杯_算法基础练习

笑望叔叔 2022-03-30 阅读 85

文章目录

#include <iostream>
using namespace std;
/*
786.求第k个数
788.逆序对的数量
790.数的三次方根
795.前缀和
796.子矩阵的和
797.差分
798.差分方程 

*/

/*786.求第k个数
给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列的第k小的数是多少。
输入格式
第一行包含两个整数 n 和 k。第二行包含 n 个整数(所有整数均在1~109范围内),表示整数数列。
输出格式
输出一个整数,表示数列的第k小数。
数据范围
1≤n≤100000,1≤k≤n
输入样例:
5 3
2 4 1 5 3
输出样例:
3

快速选择算法
1.找到分界点x,q[L],q[(L + R) / 2] , q[R]
2.左边所有数left <= x,右边所有数 >= x 
3.递归排序left,递归排序right 
①k <= sl(S-left-length)  ,递归left(第k个在左边) 
②k > sl  ,(递归right) 

*/
const int N = 100010;  //多10 比如 防止数组下标越界 
int n_01,k_01;
int q_01[N];

int quick_sort(int l,int r,int k_01){
	
	if(l >= r) return q_01[l]; //找到   快排必须写 >=  因为最终可能没有数满足 
	
	int x = q_01[l], i = l - 1,j = r + 1;  //快排边界区间在两侧 
	while(i < j)
	{
		while(q_01[++i] < x);
		while(q_01[--j] > x);
		if(i < j) swap(q_01[i] , q_01[j]);	
	}
	
	int sl = j - l + 1;
	if(k_01 <= sl) return quick_sort(l,j,k_01); //第k_01个数在左边区间 
	
	return quick_sort(j + 1,r,k_01 - sl);//k_01 - sl为右区间的第几个 
	
	
} 

/* n_01 k_01 数组 
5 3
2 4 1 5 3
*/
int test_01(){
	
	cin >> n_01 >> k_01;
	
	
	
	for(int i = 0;i < n_01;i++){
		cin >> q_01[i]; 
	} 
		
	cout << quick_sort(0,n_01 - 1,k_01) << endl;
	return 0;
} 

/*788.逆序对的数量
题目:
给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。
输入格式
第一行包含整数 n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1≤n≤100000    (极限 全部逆序 , n + n - 1 + ... + 1 = n * (n - 1) / 2 == 10^12^ / 2 > int 2^32^ -->long long) 
输入样例:
6
2 3 4 5 6 1
输出样例:
5

定义:每两个数比较 , 前面的数比后面的数大 , 则为一个逆序对 
归并排序: 
1. [L,R] => [L,mid],[mid + 1,R]
2. 归并排序 [L,mid] 和 [mid + 1, R] 
3.归并排序 ,将左右两个有序序列合并成一个有序序列
逆序对比较三种情况 :
①都在左半边  merge_sort(L,mid)
②一个左半边,一个右半边  
③都在右半边 merge_sort(mid+1,r)
 
*/


typedef long long LL;
int n_02;
int q_02[N],tmp_02[N];

LL merge_sort(int l,int r){
	
	if(l >= r) return 0;
	
	int mid = l + r >> 1;
	LL res = merge_sort(l,mid) + merge_sort(mid+1,r); //排序结果相加 
	
	//归并的过程
	int k = 0,i = l,j = mid + 1;//两个指针 头 和 中间  -->分段比较排序 -- 小的放前面一段 
	while(i <= mid && j <= r){  
		if(q_02[i] <= q_02[j]) tmp_02[k++] = q_02[i++];
		else{ 
			tmp_02[k++] = q_02[j++];
			res += mid - i + 1;   //分别在左右边已经排好序的情况③,当大于1个数即比这个数后面的数都大的区间 res要 += 此情况的逆序对个数 
		}                                       
		//扫尾 (没遍历完的接在后面)
		while(i <= mid) tmp_02[k++] = q_02[i++];
		while(j <= r) tmp_02[k++] = q_02[j++];
	} 
	//物归原主 
	for(int i = l, j = 0;i <= r;i++,j++){   //q_02数组的区间 [l,r]   tmp_02[j]从0开始按位置放q_02[i] 
		q_02[i] = tmp_02[j];		
	} 
	
	return res;	
}


int test_02(){
	
	cin >> n_02;
	for(int i = 0;i < n_02;i++){
		cin >> q_02[i];
	}
	cout << merge_sort(0 , n_02 - 1) << endl;
	
	return 0;
}


/*790.数的三次方根
给定一个浮点数n,求它的三次方根。
输入格式
共一行,包含一个浮点数n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留6位小数。
数据范围
-10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000


*/

int test_03(){
	
	double x;
	cin >> x;
	
	double l = -10000,r = 10000;
	while(r - l > 1e-8)
	{
		double mid = (l + r) / 2;
		if(mid * mid * mid >= x) r = mid;
		else l = mid ;
	}
	
	printf("%lf\n",l);//lf默认保留6位小数 ,也可以加 .6 
	
	return 0;
} 

/*795.前缀和
输入一个长度为n的整数序列。
接下来再输入m个询问,每个询问输入一对l, r。
对于每个询问,输出原序列中从第l个数到第r个数的和。
输入格式
第一行包含两个整数n和m。
第二行包含n个整数,表示整数数列。
接下来m行,每行包含两个整数l和r,表示一个询问的区间范围。
输出格式
共m行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n,
1≤n,m≤100000,
-1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10

一维前缀和:
1. Si = a1 + a2 + ... + ai
2. sum(L,R)  = aL + aL+1 + aL+2 + ... + aR =     S[k] - S[i-1] 
1.预处理前缀和数组
2.用公式求区间和 
**重要总结** :凡是数组有用到下标i-1的都从1开始定义 !  dp / hash / dfs等 
*/
int a_04[N],s_04[N];
int test_04(){
	int n,m;
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		cin >> a_04[i];
	}
	//求前缀和数组 
	for(int i = 1;i <= n;i++){ //dp 等于之前的+当前值 
		s_04[i] = s_04[i - 1] + a_04[i];
	} 
	while(m--)//m次询问求前缀和 
	{
		int l,r; //输入查询区间 
		cin >> l >> r;
		cout << s_04[r] - s_04[l - 1] << endl; //差值即可 
	}
	
	return 0;
} 

/*796.子矩阵的和
输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含四个整数x1, y1, x2, y2,表示一组询问。
输出格式
共q行,每行输出一个询问的结果。
数据范围
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
?1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
思路: 
1.s[i,j]如何计算
2.(x1,x2),(x2,y2) 分别子矩阵为左上角和右下角坐标 
3. s[x2,y2](子矩阵和) = s[x1,y1](大到边界) - s[x1 - 1,y2] - s[x2,y1 - 1] + s[x1 - 1, y1 -1]  
(s[x2,y2]指x2*y2的矩阵 ,其他同理 ,**思想**:大矩阵 - 边界的矩阵(分3个部分) + 重复多减的部分)
**画图**推导dp s[i,j] = s[i-1,j] + s[i,j-1] - s[i-1,j-1] + a[i,j] 


*/
#include<cstdio>

int n_05,m_05,q_05;
int a_05[1010][1010],s_05[1010][1010];  //数组 1e12 空间直接炸 
int test_05(){
	
	scanf("%d%d%d",&n_05,&m_05,&q_05);
	
	for(int i = 1;i <= n_05;i++){
		for(int j = 1;j <= m_05;j++){
			scanf("%d",&a_05[i][j]); 
		}
	}
	
	//初始化前缀和数组
	for(int i = 1;i <= n_05;i++){
		for(int j = 1;j <= m_05;j++){
			s_05[i][j] = s_05[i - 1][j] + s_05[i][j-1] - s_05[i - 1][j-1]  + a_05[i][j]; 
		}
	}
	
	//询问次数 
	while(q_05--)
	{
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		printf("%d\n_05",s_05[x2][y2] - s_05[x2][y1 -1] - s_05[x1 - 1][y2] + s_05[x1 - 1][y1 - 1]);  //减去边界 x1 - 1对应y2 , x2对应y1 - 1 ,再加回 记忆	
	} 
	
	return 0;
}

/*797.差分 (前缀和的逆运算) 
输入一个长度为n的整数序列。
接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数n和m。
第二行包含n个整数,表示整数序列。
接下来m行,每行包含三个整数l,r,c,表示一个操作。
输出格式
共一行,包含n个整数,表示最终序列。
数据范围
1≤n,m≤100000,
1≤l≤r≤n,
-1000≤c≤1000,
-1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2

差分思想: 时间复杂度变成O(1) 
给定 a[1],a[2],a[3],a[4] ... a[n] 
**构造差分数组 b[N]** ,使得
a[i] = b[1] + b[2] + .. b[i] (**a[i]是b数组的前缀和**) 
差分核心操作:将a[L~R] 全部加上C,等价于让b[L] += C ,b[R + 1] -= C;  C是∑a[L~R] 
1.a[1~L-1]无影响 
2.a[L~R]加上了C 
3.a[R+1,N]无影响 (注意是指 数组a的值是指 [  ∑b[0~R+1] , ∑b[0~N] ]  区间值无影响(包括了加减C)! ) 
(看做在b数组上进行了n次的插入操作) 
 在b数组上 加减C,b的前缀和a整体求和b来看 是抵消不变 ,但有些段受影响 (即满足1. && 2. && 3. 就可以实现差分了) 
 如b[R+1] -= C ,a[L~R]在[  ∑b[0~L] , ∑b[0~R] ]值加C (R+1才减C) ,前缀和a[R+1~N]包括了b[R+1]不受影响-值不变 
 而前缀和a[1~L-1]即不包括b[L]又不包括b[R+1] ,完全没有影响 --等效完成了前缀和数组a的差分  
*/

int n ,m;
int a[N],b[N]; 

void insert(int l,int r,int c){
	
	b[l] += c;
	b[r + 1] -= c;
	
}
int test_06(){
	
	cin >> n >> m;
	for(int i = 1;i<= n;i++){
		cin >> a[i];
	}
	for(int i = 1;i <= n;i++){
		insert(i,i,a[i]);
	}
	
	while(m--){
		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];
	
	for(int i = 1;i <= n;i++) printf("%d ",a[i]);
	puts("");
	
	return 0;
} 

int main() {
	
	test_06(); 
	
	return 0;
}
举报

相关推荐

0 条评论