第一次刷题笔记
Acwing刷题笔记
快速排序模板
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 100010;
int q[N];
int n;
void quick_sort(int a[], int l, int r) {
if(l >= r) return;
int x = a[(l + r) /2], i = l - 1, j = r + 1;
while(i < j) {
do i++ ; while(a[i] < x);
do j--; while(a[j] > x);
if(i < j) swap(a[i], a[j]);
}
quick_sort(a, l, j);
quick_sort(a ,j + 1, r);
}
int main() {
cin >> 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++) cout << q[i] << " ";
return 0;
}
Acwing786 第k个数
https://www.acwing.com/activity/content/problem/content/820/
题目描述:给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。
思路 利用快速排序解题,快速排序每一轮结束后,以中心元素 x(下标为j)
为界,左边是小于等于x的数,右边是大于等于x的数。
- 如果
j+1-l >= k
,说明第 k 个数在中心点的左边(l, j)
,那么可以直接递归左边,右边就不用管了。 - 反正说明第k个数在中心点右边那一块,递归右边
(j+1,r)
,但k
变成k - (j - l + 1)
,即减去左边那段的数(包括中心点),当最后递归到一个点l >= r
说明找到了数,返回q[l]
。
AC代码:
#include<iostream>
using namespace std;
const int N = 100010;
int a[N];
int quick_sort(int q[],int l,int r,int k) {
if(l >= r ) return q[l];
int i = l - 1,j = r + 1, x = q[l + r >> 1];
while(i < j) {
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i], q[j]);
}
if(j + 1 -l >=k) return quick_sort(q, l, j, k);
else return quick_sort(q, j + 1, r, k - (j - l + 1));
}
int main() {
int n, m;
cin >> n >> m;
for(int i = 0 ;i< n;i++) scanf("%d",&a[i]);
cout << quick_sort(a, 0, n - 1, m);
return 0;
}
归并排序模板
分而治之的思想
思路 : 归并排序是把一些数一直分成两半,其中的一般继续分成两半....,最后合并成有序序列的过程
如:
6 2 1 5 4 3 7
(6 2 1 5) (4 3 7)
(6 2) (1 5) (4 3) 7
(6)(2) (1)(5) (4)(3)(7) 分解到单个数了,接下来就是合并
合并
(2 6) (1 5) (3 4) (7)
(1 2 5 6) (3 4 7)
(1 2 3 4 5 6 7)
#include <iostream>
#include <cmath>
using namespace std;
const int N = 100010;
int q[N];
void merge(int a[], int l1, int r1, int l2, int r2) {
int i = l1, j = l2;
int index = 0;
int temp[r2 - l1 + 1];
while (i <= r1 && j <= r2) {
if (a[i] <= a[j])
temp[index++] = a[i++];
else
temp[index++] = a[j++];
}
while (i <= r1)
temp[index++] = a[i++];
while (j <= r2)
temp[index++] = a[j++];
for (int k = 0; k < index; k++)
a[l1 + k] = temp[k];
}
void mergeSort(int A[], int left, int right) {
if (left >= right)
return;
int mid = (left + right) / 2;
mergeSort(A, left, mid);
mergeSort(A, mid + 1, right);
merge(A, left, mid, mid + 1, right);
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++)
scanf("%d", &q[i]);
mergeSort(q, 0, n - 1);
for (int i = 0; i < n; i++)
cout << q[i] << " ";
return 0;
}
逆序对的数量
题目:给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。
思路:利用归并排序合并的时候,左边两个序列都是有序的,设左右两个序列a
、 b
,i
和j
分别指向两个序列的左边。
- 当左边序列出现
a[i] > b[j]
,那么序列a
中a[i]
后面的数也会大于b[j],都是逆序数对,有r1-i+1
个。 - 因为左边序列
a[i]
后面的数已经算到逆序对中了,但是a[i]
还有用,比较下一个b[j]
。此时j++
,比较下一个数因为可能a[i] > b[j]
,也有逆序对。
#include <iostream>
#include <cmath>
#define ll long long
using namespace std;
const int N = 100010;
int q[N];
ll ans = 0;
void merge(int a[], int l1, int r1, int l2, int r2) {
int i = l1, j = l2;
int index = 0;
int temp[r2-l1+1];
while (i <= r1 && j <= r2) {
if (a[i] <= a[j])
temp[index++] = a[i++];
else
{
ans += r1 - i + 1;
// 左边数组与右边数组对比,(i,.....) a[i] > a[j], i右边的数也会满足 > a[j]
temp[index++] = a[j++];
}
}
while (i <= r1)
temp[index++] = a[i++];
while (j <= r2)
temp[index++] = a[j++];
for (int k = 0; k < index; k++)
a[l1 + k] = temp[k];
}
void mergeSort(int A[], int left, int right) {
if (left >= right)
return;
int mid = (left + right) / 2;
mergeSort(A, left, mid);
mergeSort(A, mid + 1, right);
merge(A, left, mid, mid + 1, right);
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++)
scanf("%d", &q[i]);
mergeSort(q, 0, n - 1);
cout << ans;
return 0;
}
高精度加法模板
#include <iostream>
#include <vector>
using namespace std;
vector<int> add(vector<int> &A, vector<int> &B)
{
if (A.size() < B.size()) return add(B, A);
vector<int> C;
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()
{
string a, b;
vector<int> A, B;
cin >> 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;
}
高精度减法模板
#include <iostream>
#include <vector>
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;
for (int i = 0, t = 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()
{
string a, b;
vector<int> A, B;
cin >> 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');
vector<int> C;
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;
}
高精度乘法
思路:\(a_4a_3a_2a_1a_0\) 用十进制表示是 \(a_4∗10^4+a_3∗10^3+a_2∗10^2+a_1∗10+a_0∗10^0\)
\(b_5b_4b_3b_2b_1b_0\) 表示为 \(b_5∗10^5+b_4∗10^4+b_3∗10^3+b_2∗10^2+b_1∗10+b_0∗10^0\)
那么两数相乘,可以直接用前面的数相乘,再乘以10的次方即可,且10的次方和下标的和一致,然后相加起来就可以得出结果
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
vector<int> mul(vector<int>& A, vector<int>& B) {
vector<int> C(A.size() + B.size(), 0);
for(int i = 0;i < A.size();i++)
for(int j = 0;j < B.size();j++)
C[i + j] += A[i] * B[j]; // C = C[i] * 10^i
int t = 0; // 进位
for(int i = 0; i < C.size(); i++) {
t += C[i];
C[i] = t % 10;
t /= 10;
}
while(C.size() > 1 && C.back() == 0) C.pop_back(); // 去除前导0
return C;
}
int main() {
string a,b;
cin >> a >> b;
vector<int> A,B;
for(int i = a.length() - 1; i >= 0;i--) A.push_back(a[i] - '0');
for(int i = b.length() - 1;i >= 0;i--) B.push_back(b[i] - '0');
vector<int> c = mul(A, B);
for(int i = c.size() - 1; i >= 0;i--) cout << c[i];
return 0;
}
高精度除法
https://cdn.jsdelivr.net/gh/moon-Light404/my-picGo@master/img/202112041558375.png
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> div(vector<int> A, int B, int& r) {
vector<int> C;
for(int i = 0;i < A.size();i++) {
r = r * 10 + A[i];
C.push_back(r / B);
r = r % B;
}
reverse(C.begin(), C.end()); // 逆转去除最高位的0
while(C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main() {
string s1;
cin >> s1;
int B, r = 0;
cin >> B;
vector<int> A;
for(int i = 0; i < s1.size(); i++) A.push_back(s1[i] - '0'); // 除法从高位开始算
vector<int> c = div(A, B, r);
for(int i = c.size() - 1;i >= 0;i--) cout << c[i];
cout << endl;
cout << r;
return 0;
}