重学算法第1天,希望能坚持打卡不间断.
直到学完提高课。(暂定)
预计时长三个月内,明天再来!肝就完了
2月13日,day01 打卡
学完y总的算法基础课1.3-Week1 习题课
共7题,知识点如下
快排:第k个数
归并排序:逆序对的数量
二分:数的三次方根
前缀和(一维)
子矩阵的和(二维)
差分(一维)
差分矩阵(二维)
时间复杂度O(n)
#include
using namespace std;
const int N = 100010;
// 局部变量和全部变量重名时,会优先使用局部变量,因此可以重名
int n, k;
int q[N];
int quick_sort(int l ,int r, int k) {
if (l >= r) return q[l];
int x = q[l], i = l - 1, j = r + 1; // 取分界点
while (i < j) {
while (q[ ++ i] < x);
while (q[ -- j] > x);
if (i < j) swap(q[i], q[j]);
}
int sl = j - l + 1;
if (k <= sl) return quick_sort(l, j, k);
return quick_sort(j + 1, r, k - sl);
}
int main {
cin >> n >> k;
for (int i = 0; i < n; i++) cin >> q[i];
cout << quick_sort(0, n - 1, k) << endl;
return 0;
}
逆序对的最大数量会爆int
#include
using namespace std;
typedef long long LL;
//const int N = 10010;
const int N = 1e5 + 10;
int n;
int q[N], tmp[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[i] <= q[j]) {
tmp[k ++] = q[i ++];
} else {
tmp[k ++] = q[j ++];
res += mid - i + 1;
}
}
// 扫尾
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 res;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> q[i];
cout << merge_sort(0, n - 1) << endl;
return 0;
}
报了
Segmentation Fault
只需要将const int N = 10010;
(漏写了0,数组过小)
改为const int N = 1e5 + 10;
在左边就把右边界 r 更新为mid
在右边就把左边界 l 更新为mid
保留6位小数
写while时要比有效数后面的位置多2比较保险,即1e-8
#include ;
using namespace std;
int main () {
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;
}
// 打印l或者r都能ac
printf("%lf\n", l); // %lf默认6位
return 0;
}
一维的都很简单
下标有i-1的都从i=1开始
所以为了防止越界,数组都多定义了10个
#include
using namespace std;
const int N = 100010;
int n, m;
int a[N], s[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
// 求前缀和数组
for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
while (m --) {
int l, r;
cin >> l >> r;
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
首先要搞懂Sij的含义,左上点是(0,0),往右边和下边开始数,图中绿色就是S34
子矩阵的计算公式
s[x2][y2] = s[x2][y2] - s[x2][y1-1] -s[x1-1][y2] + s[x1-1][y1-1]
公式含义:总的 减去上面部分和左边部分,黑色区域减了2次,需要加回来
s[i,j]计算公式
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
公式含义:上侧加左侧,深蓝区域加了两次要减掉,最后加上a[i,j] (自己那个点)
#include
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], s[N][N];
int main() {
scanf("%d%d%d", &n, &m, &q); // 读入规模较大,建议使用scanf来读
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++) {
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
}
}
// 询问
while (q--) {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
// 多打了s[x2][y2] =
// printf("%d\n", s[x2][y2] = s[x2][y2] - s[x2][y1-1] -s[x1-1][y2] + s[x1-1][y1-1]);
printf("%d\n", s[x2][y2] - s[x2][y1-1] -s[x1-1][y2] + s[x1-1][y1-1]);
}
return 0;
}
一维差分
将O(n)操作降到了O(1)
前缀和有两个核心操作
差分只有一个
#include
using namespace std;
const int N = 100010;
int n, m; // n:数组长度 m:操作个数
int a[N], b[N]; // a:原数组 b差分数组
void insert(int l, int r, int c) {
b[l] += c;
b[r+1] -= c;
}
int main() {
// 读取原数组
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]);
return 0;
}
式子含义:
将b[x1,y1]右下侧整个区域前缀和都加c
再把右侧和下侧的减去C
黑色区域减了2次,加回来
最后黄色区域就是前缀和差分完的区域
#include
using namespace std;
const int N = 1010;
int n, m, q; // n m:边长, q:操作个数
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[x1][y2+1] -= c;
b[x2+1][y1] -= c;
b[x2+1][y2+1] += c;
}
int main() {
// 输入输出较多,用scanf读
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;
scanf("%d%d%d%d%d", &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++) {
a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + b[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
printf("%d ", a[i][j]);
}
//puts(""); 与下面换行等同
cout << endl;
}
return 0;
}
核心公式:
a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + b[i][j];
跟前缀和公式一样的
其实for循环有好多步可以合在一起写
但刚学的时候,分开写看的更清楚一些,以后熟练了再合起来