文章目录
一、冒泡排序
- 冒泡排序的模板
void maopao(int b[],int len){
for(int i=0;i<len-1;i++){
for(int j=0;j<len-i-1;j++){
if(b[j]>b[j+1]){
int t=b[j];
b[j]=b[j+1];
b[j+1]=t;
}
}
}
}
1、给定交换次数求最短字符串
- 按字典序的通用模板
#include <iostream>
using namespace std;
int main()
{
// 请在此输入您的代码
string str;
int k=100; //交换次数
int n=0; //字符串长度
for(;k>n*(n-1)/2;n++); //求n的大小 冒泡排序的交换次数公式n*(n-1)/2
str+='a'+k-(n-1)*(n-2)/2; //求第一个字符
for(int i='a'+n-1;i>='a';i--){
if(i!=str[0]){
//完全逆序————当等于第一个字符时跳过
str+=i;
}
}
cout<<str;
return 0;
}
二、公约数
1、给定值n求abc之积=n的个数
- 排列——即考虑顺序
- 思路:现求n的所有约数,用数组存储,之后遍历数组即可
#include <iostream>
using namespace std;
int main()
{
// 请在此输入您的代码
long long n=2021041820210418;
int res=0;
int cnt=0;
long long arr[1000000];
for(long long i=1;i*i<=n;i++){
//求n的所有约数,并用数组arr保存,注意此处的循环结束条件——i*i
if(n%i==0){
arr[cnt++]=i;
if(n/i!=i){
//一次存放除数和被除数
arr[cnt++]=n/i;
}
}
}
for(long long i=0;i<cnt;i++){
//遍历存放约数的数组
for(long long j=0;j<cnt;j++){
for(long long k=0;k<cnt;k++){
if(arr[i]*arr[j]*arr[k]==n)res++; //将约数组合,若之积为n则res+1
}
}
}
cout<<res;
return 0;
}
注意:在求n的约数时,循环结束的条件是 i * i
2、求最大公约数
递归版辗转相除法
int gcd(int a,int b)
{
if(a<b) //保证a大于b
{
swap(a,b);
}
if(a%b == 0)
{
return b;
}
return gcd(b,a%b); //除数和余数作为参数继续运算
}
三、素数
1、判断是否为素数
//判断是否是素数
int f(int x){
for(int i=2;i<x;i++){
if(x%i==0)
return 0;
}
return 1;
}
2、给定长度求等差素数的公差
#include <iostream>
using namespace std;
bool check(int n){
//判断素数
for(int i=2;i<n;i++){
if(n%i==0)return false;
}
return true;
}
int main()
{
// 请在此输入您的代码
int cnt=1; //等差数列的长度
int nn=10; //规定的等差数列长度
for(int i=2;i<10000;i++){
//先确定等差数列的第一个值
if(check(i)){
//若为素数,便将其假设为结果的第一个素数
for(int d=1;d<1000;d++){
//尝试公差,由大到小依次尝试
while(check(i+cnt*d)){
//以当前公差,尝试求连续的10个素数
cnt++;
if(cnt==nn){
cout<<d;
return 0;
}
}
cnt=1; //若不满足便有效等差素数回到原值
}
}
}
return 0;
}
四、前缀异或和
1、求子数组两两减去2的k次方后变为0
- 由于每次操作要减去两个数的 2的k次方,那么所有数中,2的k次方出现的次数之和必须是偶数。换一种说法,就是所有数的异或和必须是 0
例题:给你一个下标从 0 开始的整数数组nums 。每次操作中,你可以:
选择两个满足 0 <= i, j < nums.length 的不同下标 i 和 j 。
选择一个非负整数 k ,满足 nums[i] 和 nums[j] 在二进制下的第 k 位(下标编号从 0 开始)是 1 。
将 nums[i] 和 nums[j] 都减去 2k 。
如果一个子数组内执行上述操作若干次后,该子数组可以变成一个全为 0 的数组,那么我们称它是一个 美丽 的子数组。
请你返回数组 nums 中 美丽子数组 的数目。
子数组是一个数组中一段连续 非空 的元素序列。
class Solution {
public:
long long beautifulSubarrays(vector<int>& nums) {
int n = nums.size();
unordered_map<int, int> cnt;
cnt[0] = 1;
int now = 0;
long long ans = 0;
for (int i = 0; i < n; i++) {
now ^= nums[i]; //求前缀异或
ans += cnt[now]; //有几个相等的前缀异或值,便有几个满足要求的子数组
cnt[now]++; //记录前缀异或相等的值的个数
}
return ans;
}
};
四、前缀和和差分数组
1、前缀和
1.1、一维前缀和
使用前缀和的情况:
求连续的和
解决办法:使用一维数组s[i]记录前i项和,若是求[L,R]区间的值,则s[R]-s[L-1];
前i项和的代码
for(int i=1;i<n){
cin>>s[i];
s[i]+=s[i-1];
}
例题:给定连续的数值,允许跳过k段距离的数,求剩余数值和的最小的情况
解题步骤:
- 先求前缀和
- 求区间为k的最大值
for(int i=k;i<n;i++){
long long res=0;
res=sum[i]-sum[i-1];
maxx=max(res,maxx);
}
- 最终结果s[n-1]-maxx;
1.2、二维前缀和
- s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+s[i][j];
- 求某个子矩阵所有数值的和
- 所求=s[x2][y2]-s[x1][y2]-s[x2][y1]+s[x1][y1]
- 子矩阵的边长=x2-x1,y2-y1;
- 例题:此题规定了边长长度为c
- 例题二:不固定边长求子矩阵累加和最大的那个
2、差分算法
- 求差分数组
void insert(int l, int r, int c)
{
b[l] += c;
b[r+1] -= c;
}
- 首先计算起始成分数组,for循环遍历每一项
for(int i = 1; i <= n; i ++)
{
insert(i,i,a[i]);
}
- 若是需对原数组某个区段[L,R]加减值时,只需让差分数组第L位加上该值,第R+1位减去该值,再求前缀和便可得到改变后的数组
while(m--) //m代表操作m次
{
int l,r,c; //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]; //求差分数组每项的前缀和,便得到对应下标数组的值
printf("%d ",b[i]);
}
例题:
- 关键:比较列出关系式——
min(A\*n,B*n+c)
- n为对应路段经过的次数——将先后输出的城市序号当作区间的左右边界,其中经过的路段在原基础上+1——刚好满足差分思想
#include<bits/stdc++.h>
using namespace std;
int a[1005]; //存储原数组——可有可无
int b[1005]; //记录差分数组——此处使用一个数组,先表示差分数组,进行前缀和后表示经过变化后的数组
void in