基本算法
快速排序
分治思想 双指针运算
一,选取基准(一般是以中间为基准)
二,选取做边界 i 和有边界 j ,不断与所选的基准值进行比较,直到找到比基准值大或小的值,进行二者的交换
三,递归同上处理基准值的左右区间
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int q[N];
int n;
void quick_sort(int q[],int l,int r)
{
if(l >= r ) return ;
int i = l - 1 ,j = r + 1 , mid = q[l+r >> 1];
while(i < j)
{
do i ++; while(q[i] < mid );
do j --; while(q[j] > mid );
if(i < j) swap(q[i],q[j]);
}
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
int main(void)
{
cin >> n;
for(int i = 0 ; i < n ; i++) cin >> q[i];
quick_sort(q,0,n-1);
for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);
return 0;
}
归并排序
分治思想 区间划分
一,不断进行区间的划分缩小,直到划分成最小区间(长度为一)
二,进行递归处理,定义出一个临时数组用来存放区间左右两侧的比较大小后的结果
三,把临时数组的值赋予到原数组中,即把原两个数组合二为一
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n;
int q[N],tmp[N];
void merge_sort(int q[],int l,int r)
{
if(l >= r) return ;
int mid = l + r >> 1;
merge_sort(q,l,mid),merge_sort(q,mid + 1,r);
int k = 0;
int i = l,j = mid + 1;
while(i <= mid && j <= r)
{
if(q[i] >= q[j]) tmp[k++] = q[j ++];
else tmp[k++] = q[i++];
}
while(i <= mid) tmp[k++] = q[i++] ;
while(j <= r) tmp[k++] = q[j++] ;
for(int i = l,j = 0 ; i <= r; i ++ , j ++)
{
q[i] = tmp[j];
}
return ;
}
int main(void)
{
cin >> n;
for(int i = 0 ; i < n ; i++) cin >> q[i];
merge_sort(q,0,n-1);
for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);
return 0;
}
二分
双指针运算
一,先取到两个可以包含所求区间的左右边界的变量
二,定义出两个变量的中值
三,进行中值的可行性检查,以此来进行缩小区间
tips:区间划分时,if 后跟 l 时 ,需要进行 mid + 1 ;
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int q[N];
int n,qq;
int main(void)
{
cin >> n >> qq;
for(int i = 0 ; i < n ; i ++) scanf("%d",&q[i]);
int k;
while(qq --)
{
cin >> k;
// 左边的第一个
int l = 0 , r = n - 1;
while(l < r)
{
int mid = l + r >> 1;
if(q[mid] >= k) r = mid;
else l = mid + 1 ;
}
if(q[l] != k)cout << "-1 -1" << endl;
else{
cout << l << " ";
l = 0, r = n - 1;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(q[mid] <= k )l = mid;
else r = mid - 1;
}
cout << r << endl;
}
}
return 0;
}
高精度
为了防止出错误,一般都会加上去除前导 0 操作
高精度加法
一,利用字符串输入近两个长数字串,并利用 vector 来进行存储 转换后的字符串(倒序存储)
二,进行两个 vector 大小的判断,从而保证选取最大的数据范围
三,在 add 函数中定义出一个变量 t 用于上一次加法之后多余的十位数
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
vector<int>add(vector<int> a,vector<int> b)
{
vector<int> c;
if(a.size() < b.size()) return add(b,a);
int t = 0;
for(int i = 0 ; i < a.size(); i ++)
{
t += a[i];
if(i < b.size()) t += b[i];
c.push_back(t%10);
t /=10;
}
if (t) c.push_back(t);
return c;
}
int main(void)
{
string a,b;
cin >> a >> b;
vector <int>A,B;
for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');
auto C = add(A, B);
for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
cout << endl;
return 0;
}
高精度减法
一,由于长度和大小不同,为了防止出现负号的情况,需要用 cmp 函数比较一下
二,用 t 来表示上一次计算的借位
三,每次会进行 (t+10) % 10 操作 防止推入负数
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool cmp(vector<int> a,vector <int> b)
{
if(a.size() != b.size()) return a.size() > b.size();
for(int i = a.size() - 1 ;i >= 0 ; i --)
{
if(a[i] != b[i])
return a[i] > b[i];
}
return true;
}
vector<int> sub(vector<int> a,vector <int> b)
{
vector <int> c;
int t = 0;
for(int i = 0 ; i < a.size() ; i ++)
{
t = a[i] - t;
if(i < b.size()) t -= b[i];
c.push_back((t+10) % 10);
if(t < 0) t = 1;
else t = 0;
}
while(c.size() > 1 && c.back() == 0) c.pop_back();
return c;
}
int main(void)
{
string a,b;
cin >> a>> b;
vector<int> A,B,C;
for(int i = a.size() - 1 ; i >= 0 ; i --) A.push_back(a[i] - '0');
for(int i = b.size() - 1 ; i >= 0 ; i --) B.push_back(b[i] - '0');
if(cmp(A,B)) C = sub(A,B);
else{
C = sub(B,A);
cout << "-";
}
for(int i = C.size() - 1 ; i >= 0 ; i --) cout << C[i] ;
cout << endl;
return 0;
}
高精度乘法
一,当 t 不为 0 的时候,需要继续进行操作
二,去除前导 0
三,需要判断是否再 a 的 size 内
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> mul(vector<int> a,int b)
{
vector <int> c;
int t = 0;
for(int i = 0 ; i < a.size() || t; i ++)
{
if(i < a.size())t = t + a[i] * b;
c.push_back(t % 10);
t = t / 10;
}
while(c.size() > 1&& c.back() == 0) c.pop_back();
return c;
}
int main(void)
{
string a;
int b;
cin >> a;
cin >> b;
vector<int> A,C;
for(int i = a.size() - 1 ; i >= 0 ; i --) A.push_back(a[i] - '0');
// for(int i = b.size() - 1 ; i >= 0 ; i --) B.push_back(b[i] - '0');
C = mul(A,b);
for(int i = C.size() - 1 ; i >= 0 ; i--) cout << C[i];
return 0;
}
#include <iostream>
#include <vector>
using namespace std;
vector<int> mul(vector<int> &A, vector<int> &B) {
vector<int> C(A.size() + B.size(), 0); // 初始化为 0,且999*99最多 5 位
for (int i = 0; i < A.size(); i++)
for (int j = 0; j < B.size(); j++)
C[i + j] += A[i] * B[j];
int t = 0;
for (int i = 0; i < C.size(); i++) { // i = C.size() - 1时 t 一定小于 10
t += C[i];
C[i] = t % 10;
t /= 10;
}
while (C.size() > 1 && C.back() == 0) C.pop_back(); // 必须要去前导 0,因为最高位很可能是 0
return C;
}
int main() {
string a, b;
cin >> a >> b; // a = "1222323", b = "2323423423"
vector<int> A, B;
for (int i = a.size() - 1; i >= 0; i--)
A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i--)
B.push_back(b[i] - '0');
auto C = mul(A, B);
for (int i = C.size() - 1; i >= 0; i--)
cout << C[i];
return 0;
}
高精度除法
一,需要从高位数开始进行运算
二,每次都是取余操作
三,结束后需要 reverse 一下,可以更好匹配前面的值
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> divv(vector<int> a,int b,int & r)
{
vector<int> c;
for(int i = a.size() - 1 ; i >= 0 ; i --)
{
r = r * 10 + a[i];
c.push_back(r / b);
r = r % b;
}
reverse(c.begin(),c.end());
while(c.size() > 1 && c.back() == 0) c.pop_back();
return c;
}
int main(void)
{
string a;
int b;
cin >> a >> b;
vector<int> A,c;
for(int i = a.size() - 1 ; i >= 0 ; i -- ) A.push_back(a[i] - '0');
int r = 0;
c = divv(A,b,r);
for(int i = c.size() - 1 ; i >= 0 ; i --) cout << c[i];
cout << endl;
cout << r << endl;
return 0;
}
离散化
前缀和和差分
前缀和和差分操作都需要数组下标从 1 开始
一维前缀和
可以利用原数组自加来取到前缀和数组
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 +10;
int a[N];
int s[N];
int n,m;
int main(void)
{
cin >> n >> m;
for(int i = 1 ; i <= n ; i ++) cin >> a[i];
for(int i = 1 ; i <= n ; i++) a[i] += a[i-1];
while(m --)
{
int l,r;
cin >> l >> r;
cout << a[r] - a[l-1] << endl;
}
return 0;
}
二维前缀和
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int a[N][N];
int s[N][N];
int n,m,q;
int main(void)
{
cin >> n >> m >> q;
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++)
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
while(q--)
{
int x1,x2,y1,y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << s[x2][y2] - s[x2][y1-1] - s[x1 - 1][y2] + s[x1-1][y1-1] << endl;
}
return 0;
}
一维差分
差分数组就是前缀和数组的逆数组,他的前 n 项和就是前缀和数组
可以直接用插入的方式来求出差分数组,一般用于区间的加减
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N];
void insert(int l,int r,int c)
{
b[l] = b[l] + c;
b[r+1] = b[r+1] - c;
}
int main(void)
{
int n,m;
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++) b[i] += b[i-1];
for(int i = 1;i<=n;i++) cout<<b[i]<<" ";
}
二维差分
#include <iostream>
using namespace std;
const int N = 1010;
int a[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
b[x1][y1] += c;
b[x2 +1][y1] -= c;
b[x1][y2+1] -=c;
b[x2+1][y2+1] +=c;
}
int main(void)
{
int n,m,q;
cin>>n>>m>>q;
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++)
insert(i,j,i,j,a[i][j]);
while(q--)
{
int x1,x2,y1,y2,c;
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++)
b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ ) printf("%d ", b[i][j]);
puts("");
}
}
双指针
一,双指针一般用于处理线性即具有单调性的操作
二,一般模板如下
#include <iostream>
using namespace std;
const int N = 1e5 +10;
int q[N];
int flag[N];
int main(void)
{
int n;
cin>>n;
int ans = 0;
for(int i = 0;i<n;i++) cin>>q[i];
for(int i = 0,j = 0;i<n;i++)
{
flag[q[i]]++;
while(j<=i && flag[q[i]]>1)
{
flag[q[j]]--;
j++;
}
ans = max(ans,i-j+1);
}
cout<<ans<<endl;
return 0;
}
位运算
(lowbit) O(nlogn)O(nlogn) 使用lowbit操作,进行,每次lowbit操作截取一个数字最后一个1后面的所有位,每次减去lowbit得到的数字,直到数字减到0,就得到了最终1的个数,
lowbit原理 根据计算机负数表示的特点,如一个数字原码是10001000,他的负数表示形势是补码,就是反码+1,反码是01110111,加一则是01111000,二者按位与得到了1000,就是我们想要的lowbit操作
#include <iostream>
using namespace std;
const int N = 1e6 +10;
int q[N];
int n;
int main(void)
{
cin >> n;
while(n--)
{
int x;
cin >> x;
int res = 0;
while(x > 0) x = x - (x&-x),res ++;
cout << res <<" ";
}
return 0;
}