仅个人记录方便复习
排序
快速排序
思想——分治
1、确定分界点 以数为分界点, 这与归并排序不同,归并排序以中间位置为分界点
2、调整区间 使得左边都小于等于q[l] 右边大于等于q[l]
3、递归处理左右两边
void quick_sort(int q[],int l,int r){
int x=q[l+r>>1],i=l-1,j=r+1; //分界点
if (l >= r)return; //退出递归条件
while(i<j){ //调整
do i++;while(q[i]<x);
do j--;while(q[j]>x);
if(i<j)swap(q[i],q[j]);
}
quick_sort(q,l,j); //递归处理左右两边
quick_sort(q,j+1,r);
}
//测试
#include<iostream>
#include<algorithm> //swap()
using namespace std;
const int N=1e5+10;
int n;
int q[N];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for(int i=0;i<n;i++)printf("%d",q[i]);
return 0;
归并排序
思想——分治
1、确定分界点 mid=l+r>>1
2、先递归两边
3、归并(难点)
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N],temp[N];
int 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, i = l, j = mid + 1;
while (i<=mid&& j<=r)
if (q[i] <= q[j])temp[k++] = q[i++];
else temp[k++] = q[j++];
while (i <= mid)temp[k++] = q[i++];
while (j <= r)temp[k++] = q[j++];
for (int i = l, j = 0; i <= r; i++,j++)q[i] = temp[j];
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++)scanf("%d", &a[i]);
merge_sort(a, 0, n - 1);
for (int i = 0; i < n; i++)printf("%d ", a[i]);
return 0;
}
二分
整数二分
bool check(int x)
int bsearch_1(int l,int r){ // 区间[0,mid][mid+1,r]
while(l<r){
mid = l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
return l;
}
int bsearch_2(int l,int r){ //区间 [0,mid-1][mid,r]
while(l<r){
mid = l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
return l;
}
浮点数二分
double fbsearch(double l,double r){
const double eps=1e-6; //精度
while(r-l>eps){
double mid = l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
return l;
}
高精度
A+B A-B (A B 大小1e6bit)
A*k A/k k很小 k<=10000
大整数存储
使用数组来存每一位,位权小的存在数组低位,如12345,5在数组[0],4在[1]…
//高精度加法
//C=A+B
#include <vector>
vector<int> add(vector<int>& A, vector<int>& B) { //使用引用,不用拷贝一份,更快
vector<int> C;
int t = 0;//进位
for (int i = 0; i < A.size() || i < B.size();i++) {
if (i < A.size()) t += A[i];
if (i < B.size())t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t)C.push_back(1); //(如果最高位有进位 **注意**
return C;
}
int main() {
string a, b;
vector<int>A, B;
cin >> a >> b; //a="123456"
for (int i = a.size(); i >= 0; i--)A.push_back(a[i]-'0'); //转整形 逆序 A=[6,5,4,3,2,1]
for (int i = b.size(); i >= 0; i--)B.push_back(b[i] - '0');
auto C = add(A,B);
for (int i = C.size() - 1; i >= 0; i--)printf("%d", C[i]);
return 0;
}
//高精度减法
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; //A=B
}
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(); //(去掉前导零,高位在数组C后面 (C.size>1)确保相等时结果为0仍输出
return C;
}
int main() {
string a, b;
vector<int>A, B,C;
cin >> a >> b; //a="123456"
for (int i = a.size(); i >= 0; i--)A.push_back(a[i] - '0'); //转整形 逆序 A=[6,5,4,3,2,1]
for (int i = b.size(); i >= 0; i--)B.push_back(b[i] - '0');
if (cmp(A, B))
{
auto C = sub(A, B);
}
else {
printf("-");
auto C = sub(B, A);
}
for (int i = C.size() - 1; i >= 0; i--)printf("%d", C[i]);
return 0;
}
//高精度乘法
vector<int> mul(vector<int>& A, int b) {
vector <int> C;
int t = 0;//进位
for (int i = 0; i < A.size() - 1||t; i++) // ||t 最高位有进位 注意体会
{
t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
return C;
}
int main() {
string a;
int b;
vector <int> A;
cin >> a >> b;
for (int i = a.size() - 1; i >= 0; i--)A.push_back(a[i] - '0');
auto C = mul(A, b);
for (int i = C.size() - 1; i >= 0; i--)printf("%d", C[i]);
}
//高精度除法
#include<algorithm>
vector<int> div(vector<int>& A, int b, int &r) {
vector<int> C; //123//13
r = 0;
for (int i = A.size() - 1; i >= 0; i--) {
r = r * 10 + A[i];
C.push_back(r / b); //商
r %= b; //余数
}
reverse(C.begin(), C.end()); //逆序 低位在前,保持统一
while (C.size() > 1 && C.back() == 0)C.pop_back(); //去前导零
return C;
}
int main() {
string a; //12345
int b;
cin >> a >> b;
vector<int> A; //A{54321
for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0'); //只有除法时除法高位放前面,考虑到题目一般加减乘除都有,加减乘都是从低位开始,故方便统一,除法也高位放后面
int r = 0;
auto C = div(A, b, r);
for (int i = C.size() - 1; i >= 0; i--)printf("%d", C[i]);
cout << endl << r << endl;
return 0;
}
前缀和与差分
将O(n) 优化为 O(1)
前缀和可以在O(1)时间内查询统计任意区间之和
差分可看作前缀和的“逆运算”,可以在O(1)时间操作任意区间
树状数组插入和查询都可以优化到O(logn)。差分和前缀和适合用在查询或修改次数十分巨大的时候,当修改和查询在同一复杂度时适合用树状数组
//前缀和
//构造 s[i] = s[i - 1] + a[i];
//求区间和 s[l]-s[r-1]
//二维情况
//构造 s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
//求小矩形面积 s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]
---------------------------------------------------------------------------------------------
//差分
//一维 核心b[l] += c; b[r + 1] -= c; 求一维数组前缀和得到原数组 b[i] += b[i - 1]; cout<<b[i]
//二维 核心b[x1][y1] += c;
// b[x2 + 1][y1] -= c;
// b[x1][y2 + 1] -= c;
// b[x2 + 1][y2 + 1] += c;
// 求二维数组前缀和得到原数组 b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
//前缀和 求[r,l]区间长度 公式 s[l]-s[r-1]; s0=0 少写一个判断条件 统一逻辑运算式
int n, m;
int a[N], s[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i]; //前缀和的初始化
while (m--) //区间长度的计算 m询问次数
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]); // sr - sl-1
}
return 0;
}
//前缀和 二维情况
//思想 容斥原理 求小矩形面积 以(1,1)为原点 s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j]
// 求(x2,y2) 到(x1,y1)矩形面积
const int N = 1010;
int n, m, q;
int a[N][N], s[N][N];
int main() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);
for (int i = 1; i <= n; i++) //初始化 s数组 求前缀和
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--) //q次询问 求部分和 即子矩阵
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1]s[y1-1])
}
return 0;
}
//差分 前缀和的逆运算 //作用,维护修改区间 原来需要O(n) 现在只需O(1)
//题解中的做法 方便理解 但是感觉yxc的方法更逻辑更系统 insert
vector<int> s(100010, 0), b(100010, 0); //初始化为0
int main() {
int m, n;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> s[i]; //原数组
for (int i = 1; i <= n; i++) b[i] = s[i] - s[i - 1]; //差分数组
while (m--) {
int l, r, c;
cin >> l >> r >> c;
b[l] += c;
b[r + 1] -= c;
}
for (int i = 1; i <= n; i++) { //将修改区间后的原数组 输出
b[i] = b[i - 1] + b[i]; //b1 = b1+b0 b2=b1+b2=b0+b1+b2 即s2
cout << b[i] << ' ';
}
return 0;
}
//yxc讲的代码
const int N = 100010;
int n, m;
int a[N], b[N];
void insert(int l, int r, int c) { //核心 初始化时视为边界情况
b[l] += c;
b[r + 1] -= c;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)insert(i, i, a[i]);
while (m--) {
int l, r, c;
scanf("%d%d%d", &l, &l, &c);
insert(l, r, c);
}
for (int i = 1; i <= n; i++)b[i] += b[i - 1];
for (int i = 1; i <= n; i++)printf("%d", b[i]);
return 0;
}
//二维情况
const int N = 1000;
int n, m, q; //n行 m列 q个操作 将x1,y1,x2,y2选中的子矩阵中的元素+c 并输出完成操作后的矩阵
int a[N][N], b[N][N]; //a原矩阵 b差分矩阵
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() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &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, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2;
insert(x1, y1, x2, y2, c);
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) //保存修改后的原数组 类比一维方便理解 b[i]=b[i-1]+b[i] b1=b1+b0 b2=b1+b2=b0+b1+b2
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]);
printf(" ");
}
}
双指针算法
通过一些规律性质 如"单调性" 将朴素做法O(n^2)优化为O(n)
for(int i=0;j=0;i<n;i++)
{
while(j<i&&check(i,j)) j++;
//每道题目具体逻辑
}
//朴素做法
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
例、输入一个字符串 将其中每个单词输出出来并换行 如abc def gh 输出 abc\ndef\ngh
int main() {
string a;
cin >> a;
for (int i = 0; i < a.size(); i++) {
int j = i;
while (j < n && a[j]!=' ')j++;
for (int k = i; k < j; k++)cout<<a[k];
cout << endl;
i = j;
}
return 0;
}