int n; int q[N]; voidquick_sort(int q[],int l,int r){ if(l >= r) return; int x = q[l],i = l-1, j = r+1;//不能用q[r] 会有边界问题 递归死循环 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); } intmain(){ 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]); return0; }
voidmerge_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]) tmp[k++] = q[i++]; else tmp[k++] = q[j++]; while (i <= mid) tmp[k++] = q[i++]; while (j <= r) tmp[k++] = q[j++]; for (i = l,j = 0; i <= r; i ++, j ++) q[i] = tmp[j]; } intmain(){ scanf("%d",&n); for(int i = 0;i < n; i ++) scanf("%d",&q[i]); merge_sort(q, 0, n - 1); for(int i = 0; i < n;i ++) printf("%d",q[i]); return0; }
整数二分法(一定有解,需要处理边界)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
//区间[l,r]被划分成[l,mid]和[mid+1,r]时使用 intbsearch_1(int l, int r){ while(l < r){ int mid = l + r >> 1; if (check(mid)) r = mid;//check()判断mid是否满足性质 else l = mid +1; } return l; }
//区间[l,r]被划分成[l,mid-1]和[mid,r]时使用 intbsearch_2(int l, int r){ while(l < r){ int mid = l + r + 1>> 1; if (check(mid)) l = mid;//check()判断mid是否满足性质 此处为一个表达式 else r = mid -1 ; } return l; }
浮点数二分法(不需要处理边界)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
//此处开平方为例 #include<iostream>
usingnamespace std;
intmain(){ double x; cin >> x; double l = 0, r = x; while(r - l > 1e-8){ double mid = (l + r) / 2; if (mid * mid >= x) r = mid; else l = mid; } printf("%lf\n",l); return0; }
int son[N][26], cnt[N], idx; // 0号点既是根节点,又是空节点 // son[][]存储树中每个节点的子节点 // cnt[]存储以每个节点结尾的单词数量
// 插入一个字符串 voidinsert(char *str) { int p = 0; for (int i = 0; str[i]; i ++ ) { int u = str[i] - 'a'; if (!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; } cnt[p] ++ ; }
// 查询字符串出现的次数 intquery(char *str) { int p = 0; for (int i = 0; str[i]; i ++ ) { int u = str[i] - 'a'; if (!son[p][u]) return0; p = son[p][u]; } return cnt[p]; }
// 交换两个点,及其映射关系 voidheap_swap(int a, int b) { swap(ph[hp[a]],ph[hp[b]]); swap(hp[a], hp[b]); swap(h[a], h[b]); }
voiddown(int u) { int t = u; if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2; if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1; if (u != t) { heap_swap(u, t); down(t); } }
voidup(int u) { while (u / 2 && h[u] < h[u / 2]) { heap_swap(u, u / 2); u >>= 1; } }
//对行进行递归 对列进行遍历 //如何保证不在一条线上,那就是依靠截距来判断,当截距相同的时候才是在同一条线上 // i为列号 列号即为y | y = -x + b b = i + u // u为行号 行号就是x | x = y - b b = i - u b 可能为负数需要偏移量n 即 b = u - i + n 当加了n之后其实u和i谁减谁都一样了 //⚠️坐标系顺时针旋转了90度 #include<iostream> #include<cstdio>
usingnamespace std;
constint N = 20; char g[N][N]; bool col[N],dg[N],udg[N]; int n; voiddfs(int u){ if( u == n ){ for(int i = 0; i < n; i ++){ puts(g[i]); } puts(""); return; } for (int i = 0; i < n; i ++ ){ if(!col[i] && !dg[u+i] && !udg[n-u+i]){ g[u][i] = 'Q'; col[i] = dg[u + i] = udg[n - u + i] = true; dfs(u + 1); col[i] = dg[u + i] = udg[n - u + i] = false; g[u][i] = '.'; } } } intmain(){ cin >> n; for(int i = 0; i < n; i ++ ){ for(int j = 0; j < n; j ++ ){ g[i][j] = '.'; } } dfs(0); return0; }
// d[i] 存储点i的入度 for (int i = 1; i <= n; i ++ ) if (!d[i]) q[ ++ tt] = i;
while (hh <= tt) { int t = q[hh ++ ];
for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (-- d[j] == 0) q[ ++ tt] = j; } }
// 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。 return tt == n - 1; }
树与图的深度优先遍历
树与图的宽度优先遍历
拓扑排序
4. 数学知识
1. 数论
1.1 质数
概念
在大于1大整数中,如果只包含1和本身这两个约数,就被称为质数或素数
质数的判定 - 试除法(从定义出发).
1 2 3 4 5 6 7
// 时间复杂度O(n) boolis_prime(int n){ if( n < 2 ) returnfalse; for (int i = 2; i< n; i ++ ) if(n % i == 0) returnfalse; returntrue; }
1 2 3 4 5 6 7 8 9 10 11
// 时间复杂度O(sqrt(n)) boolis_prime(int n){ if( n < 2 ) returnfalse; for ( int i = 2; i <= n / i; i ++ ) if (n % i == 0) returnfalse; returntrue; } /** 可能会出现的几种情况,例如: i <= n / i 可能被替换成 i <= sqrt(n) 这个函数会比较慢 i * i < n 当n接近于int的范围的时候, i * i 会超出 int的范围 会有溢出风险,变成负数 出现死循环 /
// 易证n 最多只有一个大于sqrt(n)的质因子 // 时间复杂度 O(sqrt(n)) 最好是logn 最坏是根号n voiddivide(int n){ for ( int i = 2; i <= n / i; i ++ ){ if ( n % i == 0 ){ int s = 0; while (n % i == 0){ n /= i; s ++; } printf("%d %d\n", i, s); } } // 执行到就是最后一个数了,如果它大于1,就是 if( n > 1 ) printf("%d %d", n, 1); puts(""); }
筛质数
1 2 3 4 5 6 7 8 9 10 11 12 13
constint N = 1000010;
int primes[N], cnt; bool st[N];
voidget_primes( int n ){ for ( int i = 2; i <= n; i ++ ){ if (!st[i]){ primes[ cnt++ ] = n ; } for ( int j = i + i; j <= n; j += i) st[j] = true; } }
埃及数学家发明定理:埃式筛法 O(nloglogn)
1 2 3 4 5 6 7 8
voidget_primes( int n ){ for ( int i = 2; i <= n; i ++ ){ if (!st[i]){ // 如果是质数 primes[ cnt++ ] = n; for ( int j = i + i; j <= n; j += i) st[j] = true; } } }
线性筛法 (用质数去筛)
1 2 3 4 5 6 7 8 9
voidget_primes(int n){ for (int i = 2; i <= n / i; i ++ ){ if(!st[i]) primes[cnt++] = i; for(int j = 0; primes[j] <= n / i; j ++ ){ st[primes[j]*i] = true; if( i % primes[j] == 0) break; } } }
2. 组合计数
3.高斯消元
4. 简单博弈论
5. 动态规划
背包问题:
0 1 背包
在前i个物品中选择 每个物品只可以被选择一次 在不超过背包体积的情况下 最大价值是多少
朴素:
状态表示 两个维度 用f[i,j]表示
集合
1 符合条件( 只从前i个物品中挑选,总体积 <= j )
2 所有集合
属性 max min 个数 通常f[i,j]所表示的值就是属性
状态计算 状态转移方程
集合划分
不包含i f [ i - 1, j ]
包含i f[ i - 1, j - $v_{i}$ ] + $w_{i}$
最终 f [ i , j ] = Max( f [ i - 1, j ] , f [ i - 1, j - $v_{i}$ ] + $w_{i}$ )