说在前面 24级的算法考试相较于23级来说,去掉了很多送分的傻瓜题,总的来说难度不是很低
大一的程设和数据结构都是要求用C做的,不讲C++,但是到了大二的算法不会C++做题会很吃力。不过这学期现学也是来得及的!从做算法题的角度上说,C++要学的东西不是很多。个人建议尽量早点学会C++(最起码在讲到图论之前!用C做图论题会吐血的
要注意,机房的电脑很可能vscode是只能运行C而运行不了C++的!要解决这个问题,你需要:
如果vscode没有compilerun只有runcode,但是点了运行报错的话,点那个报错弹窗的debug,把里面的gcc改成g++就能运行cpp代码了
当然,你也可以选择使用dev来写,但是众所周知用dev写起来会很吃力( 但dev也不能直接用,不然的话是用不了auto,for循环冒号这些语法的,也需要配置! 如果想用dev,下面的板子的c++部分的配置dev那里有完整的步骤
以上配置步骤建议自己用机房的电脑试一下
24级算法考试我还能回想到的考试题放在最后了
下面是我考试前打印出来的板子(转成PDF只有六十多页,已经算相对偏少了(
用Obsidian整理好板子后,可以用Ob的插件Table of Contents生成目录,再转成PDF,用WPS来插入页号,最后打印出来再在目录上写上对应的页号
Hexo不自带渲染数学公式的配置,所以下面的数学公式都是渲染前的样子😟,有需要的话可以复制到Obsidian(更推荐)或者typora来看
最后,重中之重!!一定要确保板子的正确性!建议整理板子的时候把代码发给AI来确认是不是正确的!考试的时候敲了半天板子,一运行发现是错的,真的会绝望的。。。
下面是板子
一些常识知识:
$o \approx <$ ; $O \approx \leq$ ; $\Theta \approx =$ ; $\Omega\approx\geq$ ; $\omega\approx>$
$1<\log n < \sqrt n < n < n\log n < n^2 < n^3 < \dots < 2^n < 3^n <\dots < n^n$
图灵机提出 “状态自动转移” 的指令逻辑,但无法存储指令、缺乏程序结构;
冯诺依曼体系通过 “程序数据存储” 解决图灵机缺陷,奠定现代计算机硬件基础,而硬件的运算规则(如逻辑 / 算术运算)本质也是算法。
领域核心人物 :
人物
核心贡献
荣誉与代表作
Donald E. Knuth(高德纳)
1. 算法与程序设计理论先驱; 2. 发明计算机排版系统 TEX/METAFONT; 3. 提出 “分析算法的双重幸福”(数学美 + 实际价值)
1974 年图灵奖; 代表作《计算机程序设计艺术》(被誉为 “计算机程序设计理论的荷马史诗”)
Nicklaus Wirth(尼古拉斯・沃斯)
1. 提出 “程序 = 数据结构 + 算法”(媲美物理学 “E=MC²”); 2. 设计 Pascal 编程语言; 3. 推动结构化程序设计
1984 年图灵奖; 奠定程序设计的核心逻辑框架
Alan Turing(图灵)
提出图灵机模型,定义 “可计算性”,为算法理论奠定数学基础
计算机科学之父,图灵奖以其命名
John Von Neumann(冯诺依曼)
提出 “程序数据存储” 思想,解决图灵机缺陷,奠定现代计算机硬件架构
冯诺依曼体系创始人
一些特殊时间复杂度:
f(n) = f(n-1) + f(n-2) $\rightarrow$ T = $(1.618)$$^n$
$T(n)=\left{ \begin{array}{l} 1 , n=1 \ T(n-1)+1 , n>1 \end{array} \right.$ $\rightarrow$ T(n) = n
$T(n)=\left{ \begin{array}{l} 1 , n=1 \ 2T(n/2)+1 , n>1 \end{array} \right.$ $\rightarrow$ T(n) = $n\log n + n$
$T(n)=\left{ \begin{array}{l} 1 , n=1 \ T(\sqrt n)+1 , n>1 \end{array} \right.$ $\rightarrow$ T(n) = $\log \log n$
$T(n)=\left{ \begin{array}{l} 1 , n=1 \ T(n/3)+T(2n/3)+n , n>1 \end{array} \right.$ $\rightarrow$ T(n) = $\Theta(n\log n)$
P、NP、NPC、NPH 1. 复杂性等级 (Complexity Classes)
P (Polynomial): 易解 。存在多项式时间算法能直接求出答案。
NP (Nondeterministic Polynomial): 易验证 。虽然不一定能快点算出来,但如果给你一个解,你可以在多项式时间内验证它是否正确。
NP-Complete (NPC): NP中最难的问题 。
特征:如果能解决任何一个 NPC 问题,就能解决所有 NP 问题。
NP-Hard (NPH): 至少和 NPC 一样难 。它不一定属于 NP(甚至可能无法验证),但所有 NP 问题都能归约到它。
2. 归约 (Reduction) 的逻辑
概念: 问题 $A \leq_p$ 问题 $B$。含义是“利用解决 $B$ 的能力来解决 $A$”。
结论: $B$ 的难度 $\geq A$ 的难度 。
若 $A$ 是难的(NPC),则 $B$ 也是难的(NP-Hard)。
若 $B$ 是简单的(P),则 $A$ 也是简单的(P)。
二、 关键人物与标志性事件
人物
考点标签
核心贡献
Rivest, Shamir, Adleman
RSA加密
利用“大数分解质因数”的计算困难性发明了非对称加密。
Stephen Cook (库克)
SAT问题 / 库克定理
证明了 SAT(布尔可满足性问题) 是第一个 NPC 问题。
Richard Karp (卡普)
21个NPC问题
通过归约法证明了团、覆盖、哈密顿路径等 21 个经典问题均为 NPC。
三、 经典例子与对比总结 选择题常通过“相似但不同”的描述设陷阱,请记住以下分类:
1. P 类问题(看到它们就选“容易”、“多项式时间可解”)
图论: 最短路径 (Dijkstra)、最小生成树 (MST)、欧拉回路(走过每条边)。
数学: 最大公约数 (GCD)、素数判定 (AKS算法)、大数相乘、模幂运算 ($a^b \bmod n$)。
其他: 字符串匹配 (KMP)、排序算法。
2. NP-Complete / NP-Hard 问题(看到它们选“目前难解”、“NPC”)
路径类: 哈密顿回路(走过每个点)、最长 简单路径、旅行商问题 (TSP)。
逻辑类: SAT、3-SAT(注:2-SAT 是 P 问题)。
集合类: 团问题 (Clique)、顶点覆盖 (Vertex Cover)、独立集问题。
数值类: 子集和问题、背包问题。
3. 特殊案例:大数分解 (Integer Factorization)
分类: 属于 NP ,但目前不确定它是否属于 NPC。
应用: RSA 算法的安全性依赖于“分解大数”没有多项式时间解法。
四、 考试易错命题点拨
关于 $P$ 与 $NP$:
错项: “NP 问题是不可解的问题”。(纠正: NP 只是没找到快速解法,但能快速验证)。
错项: “如果 $P \neq NP$,那么 NP 问题都不能在多项式时间内求解”。(纠正: P 里的问题也是 NP 问题,它们能被快速求解)。
关于 RSA:
易解的部分: 找大素数(概率算法)、模幂运算(快速幂)、求模逆元(扩展欧几里得)。
难解的部分: 已知 $n$ 求 $p, q$(大数分解)、已知密文在不知道私钥的情况下求明文。
c++ 配置dev 解决方法:开启 C++11 标准 打开编译选项: 在菜单栏点击 工具 (Tools) -> 编译选项 (Compiler Options)。
勾选命令参数: 在弹出的窗口中,选中第一个选项卡 编译器 (Compiler)。 勾选 “在连接器命令行加入以下命令” (Add the following commands when calling the compiler)。
输入命令: 在下方的输入框中填入: -std=c++11 (注意要删掉-static-lbgcc一整行,如果你想支持更现代的语法,也可以填入 -std=c++14 或 -std=c++17)。
确认保存: 点击底部的 确定 (OK)。
重新编译: 重新按下 F11 (编译运行),程序应该就能顺利通过并运行了。
字符串 1 2 3 4 5 s.substr (pos, len); s.find ("abc" ); s.find ('a' ); if (s.find ("abc" ) != string::npos)
字符串split 1 2 3 4 5 6 7 8 9 10 11 12 13 14 vector<string> split (const string& s, char delim) { vector<string> res; string cur; for (char c : s) { if (c == delim) { if (!cur.empty ()) res.push_back (cur); cur.clear (); } else { cur.push_back (c); } } if (!cur.empty ()) res.push_back (cur); return res; }
字符串去前后空格 1 2 3 4 5 6 string strip (const string &s) { int l = 0 , r = s.size () - 1 ; while (l <= r && isspace (s[l])) l++; while (l <= r && isspace (s[r])) r--; return s.substr (l, r - l + 1 ); }
二分查找 C 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 int upper_bound (int a[], int n, int x) { int left = 0 , right = n; while (left < right) { int mid = left + (right - left) / 2 ; if (a[mid] <= x) left = mid + 1 ; else right = mid; } return left; } int lower_bound (int a[], int n, int x) { int left = 0 , right = n; while (left < right) { int mid = left + (right - left) / 2 ; if (a[mid] < x) left = mid + 1 ; else right = mid; } return left; } while (left <= right) { int mid = left + (right - left) / 2 ; if (check(mid)) { ans = mid; right = mid - 1 ; } else { left = mid + 1 ; } }
C++ 1 2 3 4 5 6 7 8 9 10 11 12 auto it = upper_bound (begin, end, val); auto it = upper_bound (begin, end, val, greater <int >());auto it = lower_bound (begin, end, val); auto it = lower_bound (begin, end, val, greater <int >());
string和char* 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> #include <string> using namespace std;int main () { char * c = "hello, world" ; string s = c; cout << s << endl; printf ("%s" , s.c_str ()); }
快速幂 1 2 3 4 5 6 7 8 9 long long binpow (long long a, long long b, long long p) { long long res = 1 ; while (b) { if (b & 1 ) res = res * a % p; a = a * a % p; b >>= 1 ; } return res; }
插入排序 1 2 3 4 5 6 7 8 9 10 11 void insertSort (vector<int >& k,int n) { int temp; int j; for (int i = 1 ; i < n; i++){ temp = k[i]; for (j = i - 1 ; j >= 0 && temp < k[j]; j--){ k[j + 1 ] = k[j]; } k[j + 1 ] = temp; } }
归并排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <iostream> #include <string> using namespace std;using keytype = int ;void insertSort (keytype k[ ],int n) { keytype temp; int j; for (int i = 1 ; i < n; i++){ temp = k[i]; for (j = i - 1 ; j >= 0 && temp < k[j]; j--){ k[j + 1 ] = k[j]; } k[j + 1 ] = temp; } } int tmp[50005 ];void Merge (int a[], int l, int m, int r) { int i = l, j = m + 1 , k = i; while (i <= m && j <= r){ if (a[i] <= a[j]){ tmp[k++] = a[i++]; }else tmp[k++] = a[j++]; } while (i <= m) tmp[k++] = a[i++]; while (j <= r) tmp[k++] = a[j++]; for (int t = l; t <= r; t++){ a[t] = tmp[t]; } } void Mergesort (int a[], int l, int r) { if (l < r){ int m = (l + r) / 2 ; Mergesort (a, l, m); Mergesort (a, m + 1 , r); Merge (a, l, m , r); } } int main () { int a[5 ] = {3 , 2 , 1 , 5 , 5 }; Mergesort (a, 0 , 4 ); for (auto i : a) cout << i << ' ' ; }
计算逆序数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <string> using namespace std;int tmp[50005 ];int inv_cnt = 0 ;void Merge (int a[], int l, int m, int r) { int i = l, j = m + 1 , k = i; while (i <= m && j <= r){ if (a[i] <= a[j]){ tmp[k++] = a[i++]; }else { tmp[k++] = a[j++]; inv_cnt += m - i + 1 ; } } while (i <= m) tmp[k++] = a[i++]; while (j <= r) tmp[k++] = a[j++]; for (int t = l; t <= r; t++){ a[t] = tmp[t]; } } void Mergesort (int a[], int l, int r) { if (l < r){ int m = (l + r) / 2 ; Mergesort (a, l, m); Mergesort (a, m + 1 , r); Merge (a, l, m , r); } } int main () { int a[5 ] = {3 , 2 , 1 , 5 , 5 }; Mergesort (a, 0 , 4 ); for (auto i : a) cout << i << ' ' ; cout << endl << inv_cnt; }
k重逆序对 问满足a[i] > k * a[j]并且 i < j的k重逆序对数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <iostream> #include <string> #define int long long using namespace std;int kk;int tmp[100005 ];long long inv_cnt = 0 ;void Merge (int a[], int l, int m, int r) { for (int i = l, j = m + 1 ; i <= m; i++){ while (j <= r && (long long )a[i] > (long long )kk * a[j]){ j++; } inv_cnt += j - (m + 1 ); } int i = l, j = m + 1 , k = i; while (i <= m && j <= r){ if (a[i] <= a[j]){ tmp[k++] = a[i++]; }else { tmp[k++] = a[j++]; } } while (i <= m) tmp[k++] = a[i++]; while (j <= r) tmp[k++] = a[j++]; for (int t = l; t <= r; t++){ a[t] = tmp[t]; } } void Mergesort (int a[], int l, int r) { if (l < r){ int m = (l + r) / 2 ; Mergesort (a, l, m); Mergesort (a, m + 1 , r); Merge (a, l, m , r); } } long long a[100005 ];signed main () { int n; cin >> n >> kk; for (int i = 0 ; i < n; i++) cin >> a[i]; Mergesort (a, 0 , n - 1 ); cout << inv_cnt; }
寻找第k大的数和快速排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> #include <vector> using namespace std;int select (vector<int > &a , int left , int right , int k) { if (left == right) return a[left]; int temp = a[right]; int i = left-1 ; for (int j=left;j<=right-1 ;j++) { if (a[j]<temp) { swap (a[++i] , a[j]); } } swap (a[i+1 ] , a[right]); int partition = i+1 ; int re = partition-left+1 ; if (re == k) return a[partition]; else if (re > k) return select (a,left,partition-1 ,k); else return select (a,partition+1 ,right,k-re); } void quicksort (vector<int > &a, int left, int right) { if (left >= right) return ; int temp = a[right]; int i = left - 1 ; for (int j = left; j <= right - 1 ; j++) { if (a[j] < temp) { swap (a[++i], a[j]); } } swap (a[i + 1 ], a[right]); int partition = i + 1 ; quicksort (a, left, partition - 1 ); quicksort (a, partition + 1 , right); } int main () { vector<int > a = {4 , 2 , 6 ,32 ,234 ,23 , 2 ,34 ,1324 ,123 ,432 ,6 ,54 ,7564 ,7865 ,4 ,1324 ,1324 }; cout << select (a, 0 , a.size () - 1 , a.size ()); quicksort (a, 0 , a.size () -1 ); cout << '\n' ; for (auto i : a){ cout << i << ' ' ; } }
moore庄园投票 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <vector> using namespace std;int boyer_moore (const vector<int >& nums) { int candidate = 0 ; int count = 0 ; for (int num : nums) { if (count == 0 ) { candidate = num; count = 1 ; } else if (num == candidate) { count++; } else { count--; } } return candidate; } int main () { vector<int > nums = {2 , 3 , 2 , 1 , 3 , 2 , 2 , 2 }; cout << boyer_moore (nums) << endl; return 0 ; }
卡特兰数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std;long long f (int n) { if (n == 0 || n == 1 ){ return 1 ; } long long ans = 0 ; for (int i = 0 ; i < n; i++){ ans += f (i) * f (n - 1 - i); } return ans; } int main () { int n; cin >> n; printf ("%lld" , f (n)); return 0 ; }
高精度乘法 $O(n^2)$ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <string.h> using namespace std;char a1[1000001 ],b1[1000001 ];int a[1000001 ],b[1000001 ];int i,x,len,j,c[1000001 ];int main () { int T; cin >> T; while (T--){ for (int i = 0 ; i < 200005 ;i++){ a1[i] = b1[i] = a[i] = b[i] = c[i] = 0 ; } cin>>a1>>b1; int lena=strlen (a1); int lenb=strlen (b1); for (i=1 ;i<=lena;i++) a[i]=a1[lena-i]-'0' ; for (i=1 ;i<=lenb;i++) b[i]=b1[lenb-i]-'0' ; for (i=1 ;i<=lenb;i++) for (j=1 ;j<=lena;j++) c[i+j-1 ]+=a[j]*b[i]; for (i=1 ;i<lena+lenb;i++) if (c[i]>9 ) { c[i+1 ]+=c[i]/10 ; c[i]%=10 ; } len = lena + lenb; while (c[len] == 0 &&len > 1 ) len--; for (i=len;i>=1 ;i--) cout<<c[i]; cout << endl; } return 0 ; }
$O(nlgn)$ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 #include <bits/stdc++.h> using namespace std;const int MAXN = 1000000 + 7 ;const double PI = acos (-1.0 );struct Complex { double x, y; }; inline Complex addc (const Complex &a, const Complex &b) { return {a.x + b.x, a.y + b.y}; } inline Complex subc (const Complex &a, const Complex &b) { return {a.x - b.x, a.y - b.y}; } inline Complex mulc (const Complex &a, const Complex &b) { return {a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; } Complex a[MAXN * 3 ], b[MAXN * 3 ]; long long ans[MAXN * 3 ];int pos[MAXN * 3 ];void FFT (Complex *A, int len, int type) { for (int i = 0 ; i < len; i++) { if (i < pos[i]) swap (A[i], A[pos[i]]); } for (int L = 2 ; L <= len; L <<= 1 ) { int H = L / 2 ; Complex Wn = {cos (2 * PI / L), type * sin (2 * PI / L)}; for (int R = 0 ; R < len; R += L) { Complex w = {1 , 0 }; for (int k = 0 ; k < H; k++) { Complex t = mulc (w, A[R + k + H]); Complex u = A[R + k]; A[R + k] = addc (u, t); A[R + k + H] = subc (u, t); w = mulc (w, Wn); } } } if (type == -1 ) { for (int i = 0 ; i < len; i++) { A[i].x /= len; A[i].y /= len; } } } int main () { int T; scanf ("%d" , &T); while (T--) { pos[0 ] = 0 ; int maxLen = 1 , l = 0 ; static char s1[MAXN], s2[MAXN]; scanf ("%s %s" , s1, s2); int n = strlen (s1); int m = strlen (s2); while (maxLen < n + m + 1 ) { maxLen <<= 1 ; l++; } for (int i = 0 ; i <= maxLen; i++) { a[i].x = a[i].y = 0 ; b[i].x = b[i].y = 0 ; ans[i] = 0 ; } for (int i = 0 ; i < n; i++) a[i].x = s1[n - 1 - i] - '0' ; for (int i = 0 ; i < m; i++) b[i].x = s2[m - 1 - i] - '0' ; for (int i = 0 ; i < maxLen; i++) { pos[i] = (pos[i >> 1 ] >> 1 ) | ((i & 1 ) << (l - 1 )); } FFT (a, maxLen, 1 ); FFT (b, maxLen, 1 ); for (int i = 0 ; i < maxLen; i++) a[i] = mulc (a[i], b[i]); FFT (a, maxLen, -1 ); for (int i = 0 ; i < maxLen; i++) { ans[i] += (long long )(a[i].x + 0.5 ); ans[i + 1 ] += ans[i] / 10 ; ans[i] %= 10 ; } int p = maxLen; while (p > 0 && ans[p] == 0 ) p--; for (; p >= 0 ; p--) printf ("%lld" , ans[p]); printf ("\n" ); } return 0 ; }
矩阵链 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <algorithm> using namespace std;long long a[305 ];long long m[305 ][305 ];long long memMCMaux (int i, int j) { if (i == j) return 0 ; if (m[i][j] != -1 ) return m[i][j]; m[i][j] = LLONG_MAX; for (int k = i; k < j; k++) { long long q = memMCMaux (i, k) + memMCMaux (k + 1 , j) + a[i-1 ]*a[k]*a[j]; if (q < m[i][j]) m[i][j] = q; } return m[i][j]; } long long memMCM (int n) { for (int i = 0 ; i <= n; i++) for (int j = 0 ; j <= n; j++) m[i][j] = -1 ; return memMCMaux (1 , n); } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int n; cin >> n; for (int i = 0 ; i <= n; i++) cin >> a[i]; cout << memMCM (n) << endl; }
钢管切割 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> #include <algorithm> using namespace std;long long p[10005 ];long long dp[10005 ]; int s[10005 ]; void outp (int n) { while (n > 0 ){ cout << s[n] << ' ' ; n = n - s[n]; } } int main () { int n; cin >> n; for (int i = 1 ; i <= n; i++) cin >> p[i]; for (int i = 1 ; i <= n; i++){ for (int j = 1 ; j <= i; j++){ if (p[j] + dp[i - j] > dp[i]){ dp[i] = p[j] + dp[i - j]; s[i] = j; } } } int count = 0 ; cout << dp[n] << endl; int n0 = n; while (n > 0 ){ n = n - s[n]; count++; } cout << count << endl; outp (n0); }
流水线调度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <algorithm> using namespace std;long long p[4 ][10005 ];long long t[4 ][4 ];long long dp[4 ][10005 ];const long long INF = 1e18 ;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int T, m; cin >> T; while (T--) { cin >> m; for (int i = 1 ; i <= 3 ; i++) { for (int j = 1 ; j <= m; j++) { cin >> p[i][j]; } } for (int i = 1 ; i <= 3 ; i++) { for (int j = 1 ; j <= 3 ; j++) { cin >> t[i][j]; } } for (int i = 1 ; i <= 3 ; i++) { dp[i][1 ] = p[i][1 ]; } for (int j = 2 ; j <= m; j++) { for (int i = 1 ; i <= 3 ; i++) { dp[i][j] = INF; for (int k = 1 ; k <= 3 ; k++) { long long cost = dp[k][j - 1 ] + p[i][j] + t[k][i]; dp[i][j] = min (dp[i][j], cost); } } } long long ans = min ({dp[1 ][m], dp[2 ][m], dp[3 ][m]}); cout << ans << "\n" ; } return 0 ; }
OBST最优二叉搜索树 有查找失败概率的OBST 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <iostream> #include <vector> #include <limits> using namespace std;const int INF = 1e9 ;vector<vector<int >> root; double optimalBST (const vector<double >& p, const vector<double >& q, int n) { vector<vector<double >> e (n + 2 , vector <double >(n + 1 , 0 )); vector<vector<double >> w (n + 2 , vector <double >(n + 1 , 0 )); root.assign (n + 1 , vector <int >(n + 1 , 0 )); for (int i = 1 ; i <= n + 1 ; ++i) { e[i][i - 1 ] = q[i - 1 ]; w[i][i - 1 ] = q[i - 1 ]; } for (int l = 1 ; l <= n; ++l) { for (int i = 1 ; i <= n - l + 1 ; ++i) { int j = i + l - 1 ; e[i][j] = INF; w[i][j] = w[i][j - 1 ] + p[j] + q[j]; for (int k = i; k <= j; ++k) { double t = e[i][k - 1 ] + e[k + 1 ][j] + w[i][j]; if (t < e[i][j]) { e[i][j] = t; root[i][j] = k; } } } } return e[1 ][n]; } void printRootTree (int i, int j, int parent, bool isLeft) { if (i > j) return ; int r = root[i][j]; if (parent == -1 ) cout << "Root: " << r << "\n" ; else cout << (isLeft ? "Left child of " : "Right child of " ) << parent << ": " << r << "\n" ; printRootTree (i, r - 1 , r, true ); printRootTree (r + 1 , j, r, false ); } int main () { int n; cout << "Number of nodes: " ; cin >> n; vector<double > p (n + 1 , 0 ) ; vector<double > q (n + 1 , 0 ) ; for (int i = 1 ; i <= n; ++i) cin >> p[i]; for (int i = 0 ; i <= n; ++i) cin >> q[i]; double res = optimalBST (p, q, n); cout << "Optimal BST expected cost: " << res << "\n" ; printRootTree (1 , n, -1 , false ); return 0 ; }
无查找失败概率的OBST 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <iostream> #include <vector> #include <algorithm> using namespace std;vector<vector<int >> root (505 , vector <int >(505 , 0 )); vector<int > Tleft (505 , -1 ) ;vector<int > Tright (505 , -1 ) ;void build (int l, int r) { if (l > r) return ; int k = root[l][r]; if (l <= k - 1 ){ Tleft[k] = root[l][k - 1 ]; build (l, k - 1 ); }else { Tleft[k] = -1 ; } if (k + 1 <= r){ Tright[k] = root[k + 1 ][r]; build (k + 1 , r); }else { Tright[k] = -1 ; } } int main () { cin.tie (0 ); ios::sync_with_stdio (0 ); int n; cin >> n; vector <int > p (n + 1 , 0 ); for (int i = 1 ; i <= n; i++) cin >> p[i]; vector<vector<long long >> dp (n + 2 , vector <long long >(n + 2 , 0 )); vector<vector<long long >> sum (n + 2 , vector <long long >(n + 2 , 0 )); vector<long long > prefixSum (n + 1 , 0 ) ; for (int i = 1 ; i <= n; i++) prefixSum[i] = prefixSum[i - 1 ] + p[i]; for (int i = 1 ; i <= n + 1 ; i++) dp[i][i - 1 ] = 0 ; for (int len = 1 ; len <= n; len++){ for (int i = 1 ; i + len - 1 <= n; i++){ int j = i + len - 1 ; dp[i][j] = (long long )1 << 60 ; for (int k = i; k <= j; k++){ long long cost = dp[i][k - 1 ] + dp[k + 1 ][j] + prefixSum[j] - prefixSum[i - 1 ]; if (cost < dp[i][j]){ dp[i][j] = cost; root[i][j] = k; } } } } cout << dp[1 ][n] << endl; build (1 , n); for (int i = 1 ; i <= n; i++) cout << Tleft[i] << ' ' << Tright[i] << endl; }
01/完全背包问题 二维数组版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> #include <algorithm> using namespace std;int a[2005 ];int v[2005 ];int dp[2005 ][2005 ];int bag01 (int n, int w) { for (int i = 1 ; i <= n; i++){ for (int j = 1 ; j <= w; j++){ if (j - a[i] >= 0 ) dp[i][j] = max (dp[i - 1 ][j], dp[i - 1 ][j - a[i]] + v[i]); else dp[i][j] = dp[i - 1 ][j]; } } return dp[n][w]; } int bagfull (int n, int w) { for (int i = 1 ; i <= n; i++){ for (int j = a[i]; j <= w; j++){ dp[i][j] = max (dp[i - 1 ][j], dp[i][j - a[i]] + v[i]); } } return dp[n][w]; } int main () { int n, w; cin >> n >> w; for (int i = 1 ; i <= n; i++) cin >> a[i] >> v[i]; cout << bag01 (n, w) << endl; for (int i = 0 ;i < 2005 ; i++) fill (dp[i], dp[i] + 2005 , 0 ); cout << bagfull (n, w) << endl; }
一维数组版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> #include <algorithm> #include <vector> using namespace std;int a[2005 ]; int v[2005 ]; int dp[2005 ]; int bag01 (int n, int w) { fill (dp, dp + w + 1 , 0 ); for (int i = 1 ; i <= n; i++) { for (int j = w; j >= a[i]; j--) { dp[j] = max (dp[j], dp[j - a[i]] + v[i]); } } return dp[w]; } int bagfull (int n, int w) { fill (dp, dp + w + 1 , 0 ); for (int i = 1 ; i <= n; i++) { for (int j = a[i]; j <= w; j++) { dp[j] = max (dp[j], dp[j - a[i]] + v[i]); } } return dp[w]; } int main () { int n, w; if (!(cin >> n >> w)) return 0 ; for (int i = 1 ; i <= n; i++) cin >> a[i] >> v[i]; cout << "0/1 Knapsack: " << bag01 (n, w) << endl; cout << "Complete Knapsack: " << bagfull (n, w) << endl; return 0 ; }
01背包问题,dp[w]版本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> #include <algorithm> #include <vector> using namespace std;int main () { long long n, V; cin >> n >> V; vector<long long > v (n + 1 ) ; vector<int > w (n + 1 ) ; int wsum = 0 ; for (int i = 1 ; i <= n; i++){ cin >> v[i] >> w[i]; wsum += w[i]; } long long INF = (long long )9e18 ; vector<long long > dp (wsum + 1 , INF) ; dp[0 ] = 0 ; for (int i = 1 ; i <= n; i++){ for (long long j = wsum; j >= w[i]; j--){ if (dp[j - w[i]] != INF){ dp[j] = min (dp[j], dp[j - w[i]] + v[i]); } } } long long ans = 0 ; for (long long val = 0 ; val <= wsum; val ++){ if (dp[val] <= V) ans = max (ans, val); } cout << ans; }
最长不上升子序列/导弹拦截 O(nlgn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> #include <algorithm> #include <vector> using namespace std;int main () { vector<int > a; int x; while ((cin >> x)){ a.push_back (x); } int n = a.size (); vector<int > d1, d2; for (int i = 0 ; i < n; i++){ if (d1. empty () || d1. back () >= a[i]){ d1. push_back (a[i]); }else { auto it = upper_bound (d1. begin (), d1. end (), a[i], greater <int >()); *it = a[i]; } if (d2. empty () || d2. back () < a[i]) { d2. push_back (a[i]); } else { auto it = lower_bound (d2. begin (), d2. end (), a[i]); *it = a[i]; } } cout << d1. size () << endl; cout << d2. size () << endl; return 0 ; }
最长等差序列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int longestArithSeqLength (vector<int >& nums) { int n = nums.size (); if (n <= 2 ) return n; vector<vector<int >> dp (n, vector <int >(1001 , 1 )); int maxlen = 2 ; for (int i = 1 ; i < n; i++){ for (int j = 0 ; j < i; j++){ int diff = nums[i] - nums[j] + 500 ; dp[i][diff] = dp[j][diff] + 1 ; maxlen = max (maxlen, dp[i][diff]); } } return maxlen; }
最长公共子序列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <vector> #include <algorithm> #include <vector> #include <iostream> using namespace std;int main () { int n; cin >> n; vector<int > a (n + 1 ) ; vector<int > b (n + 1 ) ; vector<int > dp (n + 1 , 0 ) ; for (int i = 1 ; i <= n; i++) cin >> a[i]; for (int i = 1 ; i <= n; i++) cin >> b[i]; for (int i = 1 ; i <= n; i++){ int pre = 0 ; for (int j = 1 ; j <= n; j++){ int tmp = dp[j]; if (a[i] == b[j]) dp[j] = pre + 1 ; else { dp[j] = max (dp[j], dp[j - 1 ]); } pre = tmp; } } cout << dp[n]; }
编辑距离 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <algorithm> #include <vector> #include <string> using namespace std;int dp[2005 ][2005 ];int main () { string a, b; cin >> a >> b; int lena = a.size (); int lenb = b.size (); for (int j = 0 ; j <= lenb; j++){ dp[0 ][j] = j; } for (int i = 0 ; i <= lena; i++){ dp[i][0 ] = i; } for (int i = 1 ; i <= lena; i++){ for (int j = 1 ; j <= lenb; j++){ if (a[i - 1 ] == b[j - 1 ]){ dp[i][j] = dp[i - 1 ][j - 1 ]; }else dp[i][j] = min ({dp[i - 1 ][j] + 1 , dp[i][j - 1 ] + 1 , dp[i - 1 ][j - 1 ] + 1 }); } } cout << dp[lena][lenb]; return 0 ; }
区间动态规划 回文子串 回文词是一种对称的字符串。任意给定一个字符串,通过插入若干字符,都可以变成回文词。此题的任务是,求出将给定字符串变成回文词所需要插入的最少字符数。
比如 Ab3bd 插入 2 个字符后可以变成回文词 dAb3bAd 或 Adb3bdA,但是插入少于 2 个的字符无法变成回文词。
注意 :此问题区分大小写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> #include <algorithm> #include <vector> #include <string> using namespace std;int main () { string ss; cin >> ss; string s = '$' + ss; int n = ss.size (); vector<vector<int >> dp (n + 1 , vector <int >(n+1 ,0 )); for (int i = 1 ; i < n; i++){ for (int j = 1 ; j <= n - i; j++){ int k = i + j; if (s[j] == s[k]){ dp[j][k] = dp[j + 1 ][k - 1 ]; }else { dp[j][k] = min (dp[j + 1 ][k], dp[j][k - 1 ]) + 1 ; } } } cout << dp[1 ][n]; }
石子合并 题目描述 设有 N(N≤300) 堆石子排成一排,其编号为 1,2,3,⋯,N。每堆石子有一定的质量 mi (mi≤1000)。现在要将这 N 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。
输入格式 第一行,一个整数 N。
第二行,N 个整数 mi。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> #include <algorithm> #include <vector> using namespace std;int main () { int N; cin >> N; vector<int > m (N + 1 , 0 ) ; vector<int > pre (N + 1 , 0 ) ; for (int i = 1 ; i <= N; i++){ cin >> m[i]; pre[i] = pre[i - 1 ] + m[i]; } vector<vector<int >> dp (N + 1 , vector <int >(N + 1 , 100000000 )); for (int i = 1 ; i <= N; i++) dp[i][i] = 0 ; for (int k = 2 ; k <= N; k++){ for (int i = 1 ; i <= N - k + 1 ; i++){ int j = i + k - 1 ; for (int l = i; l < j; l++){ dp[i][j] = min (dp[i][j], dp[i][l] + dp[l + 1 ][j] + pre[j] - pre[i - 1 ]); } } } cout << dp[1 ][N]; }
zuma问题/回文子串数 Genos 最近在他的手机上下载了祖玛游戏。在祖玛游戏里,存在 n 个一行的宝石,第 i 个宝石的颜色是 Ci。这个游戏的目标是尽快的消灭一行中所有的宝石。
在一秒钟,Genos 能很快的挑选出这些有颜色的宝石中的一个回文的、连续的子串,并将这个子串移除。每当一个子串被删除后,剩余的宝石将连接在一起,形成一个新的行列。
你的任务是:求出把整个宝石串都移除的最短时间。
输入格式 第一行包含一个整数 n(1≤n≤500),表示宝石串的长度。第二行包含 n 个被空格分开的整数,第 i(1≤i≤n) 个表示这行中第 i 个珠子的颜色。
输出格式 输出一个整数,把这行珠子移除的最短时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <algorithm> #include <vector> using namespace std;int main () { int n; cin >> n; vector<int > a (n + 1 , 0 ) ; vector<vector<int >> dp (n + 1 , vector <int >(n + 1 , 233333333 )); vector<int > sum (n + 1 , 0 ) ; for (int i = 1 ; i <= n; i++){ cin >> a[i]; sum[i] = sum[i - 1 ] + a[i]; } for (int i = 1 ; i <= n; i++) dp[i][i] = 1 ; for (int i=1 ;i<n;i++) dp[i][i+1 ]=1 +(a[i]!=a[i+1 ]); for (int len = 3 ; len <= n; len++){ for (int i = 1 ; i <= n - len + 1 ; i ++){ int j = i + len - 1 ; if (a[i] == a[j]){ dp[i][j] = dp[i + 1 ][j - 1 ]; }else { for (int k = i; k < j; k++){ dp[i][j] = min (dp[i][j], dp[i][k] + dp[k+1 ][j]); } } } } cout << dp[1 ][n]; }
树形动态规划 没有上司的舞会 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <iostream> #include <algorithm> #include <vector> using namespace std;int happy[6005 ];int hasParent[6006 ];int dp[6005 ][2 ];vector<int > children[6005 ]; void dfs (int u) { dp[u][1 ] = happy[u]; dp[u][0 ] = 0 ; for (int v: children[u]){ dfs (v); dp[u][1 ] += dp[v][0 ]; dp[u][0 ] += max (dp[v][0 ], dp[v][1 ]); } } int main () { cin.tie (0 ); ios::sync_with_stdio (0 ); int N; cin >> N; for (int i = 1 ; i <= N; i++) cin >> happy[i]; int L, K; while (cin >> L >> K && (K || L)){ children[K].push_back (L); hasParent[L] = 1 ; } int root = 1 ; for (int i = 1 ; i <= N; i++){ if (!hasParent[i]){ root = i; break ; } } dfs (root); cout << max (dp[root][0 ], dp[root][1 ]) << endl; return 0 ; }
背包问题 二维数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int bag01 (int n, int w) { for (int i = 1 ; i <= n; i++){ for (int j = 1 ; j <= w; j++){ if (j - a[i] >= 0 ) dp[i][j] = max (dp[i - 1 ][j], dp[i - 1 ][j - a[i]] + v[i]); else dp[i][j] = dp[i - 1 ][j]; } } return dp[n][w]; } int bagfull (int n, int w) { for (int i = 1 ; i <= n; i++){ for (int j = a[i]; j <= w; j++){ dp[i][j] = max (dp[i - 1 ][j], dp[i][j - a[i]] + v[i]); } } return dp[n][w]; }
一维数组 1 2 3 4 5 6 7 8 9 10 int bag01 (int n, int w) { for (int i = 1 ; i <= n; i++) { for (int j = w; j >= a[i]; j--) { dp[j] = max (dp[j], dp[j - a[i]] + v[i]); } } return dp[w]; }
1 2 3 4 5 6 7 8 9 10 int bagfull (int n, int w) { for (int i = 1 ; i <= n; i++) { for (int j = a[i]; j <= w; j++) { dp[j] = max (dp[j], dp[j - a[i]] + v[i]); } } return dp[w]; }
分组背包 物品被分成若干组,每组中的物品只能选择一个 。也就是说,从每组物品中,你只能选择一个或者不选,但不能选择多个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int MAX = 1005 ; struct { int cnt; ll ID[MAX]; } group[MAX]; ll dp[MAX]; ll val[MAX]; ll weight[MAX]; ll group_bag (int cap, int max_group) ; int main () { int n, W; cin >> W >> n; int a, b, k, max_group = 0 ; for (int i = 1 ; i <= n; i++) { cin >> a >> b >> k; weight[i] = a; val[i] = b; group[k].ID[group[k].cnt++] = i; max_group = max (max_group, k); } cout << group_bag (W, max_group); return 0 ; } ll group_bag (int W, int max_group) { for (int i = 0 ; i <= max_group; i++) for (ll j = W; j >= 0 ; j--) for (int k = 0 ; k < group[i].cnt; k++) if (j >= weight[group[i].ID[k]]) dp[j] = max (dp[j],dp[j - weight[group[i].ID[k]]] + val[group[i].ID[k]]); return dp[W]; }
多重背包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> using namespace std; const int MAXN=1e5 +10 ; int n,W; int v[MAXN],w[MAXN]; int f[MAXN]; int main () { cin >> n >> W; int cnt = 0 ; for (int i=1 ,a,b,s; i<=n; i++) { cin>> a >> b >> s; int k = 1 ; while (k <= s){ v[++cnt] = k*a; w[cnt] = k*b; s -= k; k *= 2 ; } if (s){ v[++cnt] = s*a; w[cnt] = s*b; } } for (int i=1 ; i<=cnt; i++) for (int j=W; j>=v[i]; j--) f[j] = max (f[j], f[j-v[i]]+w[i]); cout << f[W]; return 0 ; }
贪心,最多能上多少节课 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> #include <algorithm> #include <vector> using namespace std;struct course { int a; int b; }; bool cmp (course x, course y) { if (x.b < y.b) return true ; else if (x.b == y.b) return x.a < y.a; return false ; } int main () { int n; cin >> n; vector<course> c; int a, b; for (int i = 0 ; i < n; i++){ cin >> a >> b; c.push_back ({a,b}); } sort (c.begin (), c.end (), cmp); int time_now = c[0 ].b; int all = 1 ; for (int i = 1 ; i < n; i++){ if (c[i].a >= time_now){ all += 1 ; time_now = c[i].b; } } cout << all; }
哈夫曼编码/解码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 #include <iostream> #include <vector> #include <queue> #include <map> #include <string> using namespace std;struct Node { char data; int freq; Node *left, *right; }; struct cmp { bool operator () (Node* l, Node* r) { return l->freq > r->freq; } }; string encode (string text, map<char , string>& huffmanCodes) { string encodedStr = "" ; for (char ch : text){ encodedStr += huffmanCodes[ch]; } return encodedStr; } string decode (Node *root, string encodedStr) { string decodedStr = "" ; Node* curr = root; for (char bit : encodedStr) { if (bit == '0' ) { curr = curr->left; } else { curr = curr->right; } if (!curr->left && !curr->right) { decodedStr += curr->data; curr = root; } } return decodedStr; } void generateCodes (Node *root, string code, map<char , string> &huffmanCodes) { if (!root) return ; if (root->data != '$' ){ huffmanCodes[root->data] = code; } generateCodes (root->left, code + "0" , huffmanCodes); generateCodes (root->right, code + "1" , huffmanCodes); } void buildHuffmanTree (string text) { map<char , int > freq; for (char &ch: text){ freq[ch]++; } priority_queue<Node*, vector<Node*>, cmp> pq; for (auto &pair : freq){ pq.push (new Node ({pair.first, pair.second, nullptr , nullptr })); } while (pq.size () > 1 ){ Node *left = pq.top (); pq.pop (); Node *right = pq.top (); pq.pop (); Node *newNode = new Node ({'$' , left->freq + right->freq, nullptr , nullptr }); newNode->left = left; newNode->right = right; pq.push (newNode); } Node* root = pq.top (); map<char , string> huffmanCodes; generateCodes (root, "" , huffmanCodes); cout << "Huffman Codes:" << endl; for (auto const & [ch, code] : huffmanCodes) { cout << ch << ": " << code << endl; } string encoded = encode (text, huffmanCodes); cout << "\nEncoded String: " << encoded << endl; cout << "Decoded String: " << decode (root, encoded) << endl; } int main () { string s; cin >> s; buildHuffmanTree (s); }
bfs dfs $O(V+E)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <iostream> #include <algorithm> #include <vector> #include <queue> using namespace std;void DFSUtil (const vector<vector<int >> &adj, int u, vector<bool > &visited) { visited[u] = true ; cout << u << ' ' ; for (int v : adj[u]){ if (!visited[v]){ DFSUtil (adj, v, visited); } } } void dfs (const vector<vector<int >> &adj, int start) { int V = adj.size (); vector<bool > visited (V, false ) ; DFSUtil (adj, start, visited); cout << endl; } void bfs (const vector<vector<int >> &adj, int start) { int V = adj.size (); vector<bool > visited (V, false ) ; queue<int > q; visited[start] = true ; q.push (start); while (!q.empty ()){ int u = q.front (); q.pop (); cout << u << ' ' ; for (int v : adj[u]){ if (!visited[v]){ visited[v] = true ; q.push (v); } } } cout << endl; } int main () { int n, m, u, v; cin >> n >> m; vector<vector<int >> adj (n + 1 ); for (int i = 0 ; i < m; i++){ cin >> u >> v; adj[u].push_back (v); } for (int i = 1 ; i <= n; i ++) sort (adj[i].begin (), adj[i].end ()); dfs (adj, 1 ); bfs (adj, 1 ); }
Bellman-Ford $O(VE) / O(kE)$ 可处理负权边
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <iostream> #include <vector> #include <algorithm> using namespace std;const int INF = 1e9 ;pair<vector<int >, bool > bellman_ford (int n, const vector<vector<pair<int , int >>>& adj, int s) { vector<int > dist (n + 1 , INF) ; dist[s] = 0 ; for (int i = 1 ; i <= n - 1 ; i++) { bool updated = false ; for (int u = 1 ; u <= n; u++) { if (dist[u] == INF) continue ; for (auto & edge : adj[u]) { int v = edge.first; int w = edge.second; if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; updated = true ; } } } if (!updated) break ; } for (int u = 1 ; u <= n; u++) { if (dist[u] == INF) continue ; for (auto & edge : adj[u]) { int v = edge.first; int w = edge.second; if (dist[u] + w < dist[v]) { return {dist, true }; } } } return {dist, false }; } int main () { int n, m, s; if (!(cin >> n >> m >> s)) return 0 ; vector<vector<pair<int , int >>> adj (n + 1 ); for (int i = 0 ; i < m; i++) { int u, v, w; cin >> u >> v >> w; adj[u].push_back ({v, w}); } auto [dist, hasNegativeCycle] = bellman_ford (n, adj, s); if (hasNegativeCycle) { cout << "存在负权回路\n" ; } else { for (int i = 1 ; i <= n; i++) { if (dist[i] == INF) cout << "INF " ; else cout << dist[i] << " " ; } cout << endl; } return 0 ; }
拓扑排序+松弛求最短路径 $O(V + E)$ 有向无环图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <algorithm> #include <vector> #include <queue> using namespace std;int indegree[100005 ];int main () { int n, m, u, v, w; cin >> n >> m; vector<vector<pair<int ,int >>> adj (n + 1 ); for (int i = 1 ; i <= m; i++){ cin >> u >> v >> w; adj[u].push_back ({v,w}); indegree[v]++; } queue<int > q; for (int i = 1 ; i <= n; i++){ if (indegree[i] == 0 ){ q.push (i); } } vector<int > topo; while (!q.empty ()) { int u = q.front (); q.pop (); topo.push_back (u); for (auto &e : adj[u]) { indegree[e.first]--; if (indegree[e.first] == 0 ) q.push (e.first); } } for (int i = 0 ; i < (int )topo.size (); i++) { cout << topo[i] << ' ' ; } cout << endl; vector<long long > dist (n + 1 , ((long long )1 << 60 )) ; dist[1 ] = 0 ; for (int u : topo) { if (dist[u] == ((long long )1 << 60 )) continue ; for (auto &e : adj[u]) { int v = e.first; int w = e.second; if (dist[u] + w < dist[v]) dist[v] = dist[u] + w; } } for (int i = 1 ; i <= n; i++) { if (dist[i] == ((long long )1 << 60 )) cout << -1 ; else cout << dist[i]; if (i != n) cout << ' ' ; } }
floyd算法 path(存的不是前序节点) $O(V^3)$ 多源点最短路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 vector<vector<int >> dist (n + 1 , vector <int >(n + 1 , INF)); vector<vector<int >> path (n + 1 , vector <int >(n + 1 , -1 )); for (int i = 1 ; i <= n; i++){ dist[i][i] = 0 ; path[i][i] = i; } for (int k = 1 ; k <= n; k++){ for (int i = 1 ; i <= n; i++){ for (int j = 1 ; j <= n; j++){ if (dist[i][k] + dist[k][j] < dist[i][j]){ dist[i][j] = dist[i][k] + dist[k][j]; path[i][j] = path[i][k]; } } } } 检测负环 bool hasNegativeCycle = false ;for (int i = 1 ; i <= n; ++i) { if (dist[i][i] < 0 ) { hasNegativeCycle = true ; break ; } } void printPath (int i, int j) { if (i == j) { cout << i << " " ; return ; } if (path[i][j] == -1 ) { cout << "无路径" ; return ; } cout << i << " " ; printPath (path[i][j], j); }
dj算法求最短路 邻接矩阵版 $O(V^2)$ 单源最短路(无负边),适合稠密图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <algorithm> #include <vector> using namespace std;int main () { int n, m, s; cin >> n >> m >> s; vector<vector<int >> adj (n + 1 , vector <int >(n + 1 , 1e9 )); vector<int > dist (n + 1 , 1e9 ) ; vector<int > vis (n + 1 , 0 ) ; vector<int > pre (n + 1 , -1 ) ; int u, v, w; for (int i = 0 ; i < m; i++){ cin >> u >> v >> w; adj[u][v] = adj[v][u] = w; } dist[s] = 0 ; for (int i = 1 ; i <= n; i++){ int u = -1 ; for (int j = 1 ; j <= n; j++){ if (!vis[j] && (u == -1 || dist[j] < dist[u])){ u = j; } } if (u == -1 ) break ; vis[u] = 1 ; for (int v = 1 ; v <= n; v++){ if (dist[u] + adj[u][v] < dist[v]){ dist[v] = dist[u] + adj[u][v]; pre[v] = u; } } } for (int i = 1 ; i <= n; i++){ if (dist[i] == 1e9 ) cout << "INF " ; else cout << dist[i] << " " ; } }
邻接表 $O(E \log V)$ 适合稀疏图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> #include <algorithm> #include <vector> #include <queue> using namespace std;int main () { int n, m, s; cin >> n >> m >> s; vector<vector<pair<int ,int >>> adj (n + 1 ); vector<int > dist (n + 1 , 1e9 ) ; vector<int > pre (n + 1 , -1 ) ; int u, v, w; for (int i = 0 ; i < m; i++){ cin >> u >> v >> w; adj[u].push_back ({v, w}); adj[v].push_back ({u, w}); } priority_queue<pair<int , int >, vector<pair<int , int >>, greater<pair<int , int >>> pq; dist[s] = 0 ; pq.push ({0 , s}); while (!pq.empty ()){ auto [d, u] = pq.top (); pq.pop (); if (d > dist[u]) continue ; for (auto &edge : adj[u]){ int v = edge.first; int w = edge.second; if (dist[u] + w < dist[v]){ dist[v] = dist[u] + w; pre[v] = u; pq.push ({dist[v], v}); } } } for (int i = 1 ; i <= n; i++){ cout << dist[i] << ' ' ; } }
prim最小生成树 $O(E \log V)$ 基于点,适合稠密图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include <iostream> #include <vector> #include <queue> using namespace std;const int INF = 0x3f3f3f3f ;int main () { int n, m; if (!(cin >> n >> m)) return 0 ; vector<vector<pair<int , int >>> adj (n + 1 ); for (int i = 0 ; i < m; ++i) { int u, v, w; cin >> u >> v >> w; adj[u].push_back ({v, w}); adj[v].push_back ({u, w}); } vector<int > dist (n + 1 , INF) ; vector<bool > visited (n + 1 , false ) ; priority_queue<pair<int , int >, vector<pair<int , int >>, greater<pair<int , int >>> pq; int total_weight = 0 ; int count = 0 ; dist[1 ] = 0 ; pq.push ({0 , 1 }); while (!pq.empty ()) { int d = pq.top ().first; int u = pq.top ().second; pq.pop (); if (visited[u]) continue ; visited[u] = true ; total_weight += d; count++; for (auto & edge : adj[u]) { int v = edge.first; int weight = edge.second; if (!visited[v] && weight < dist[v]) { dist[v] = weight; pq.push ({dist[v], v}); } } } if (count == n) { cout << total_weight << endl; } else { cout << "orz" << endl; } return 0 ; }
dinic算最大流/最大割 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 #include <bits/stdc++.h> #define int long long using namespace std;const int N = 205 , INF = 1e15 ;struct edge { int to; int cap; int rev; bool is_real; }; vector<edge> G[N]; int dep[N], cur[N];int n, m, S, T;void add_edge (int u, int v, int w) { G[u].push_back ({v, w, (int )G[v].size (), true }); G[v].push_back ({u, 0 , (int )G[u].size () - 1 , false }); } bool bfs () { memset (dep, -1 , sizeof dep); dep[S] = 0 ; queue<int > q; q.push (S); while (!q.empty ()) { int u = q.front (); q.pop (); for (auto &e : G[u]) { if (e.cap > 0 && dep[e.to] == -1 ) { dep[e.to] = dep[u] + 1 ; q.push (e.to); } } } return dep[T] != -1 ; } int dfs (int u, int limit) { if (u == T || limit == 0 ) return limit; for (int &i = cur[u]; i < G[u].size (); i++) { edge &e = G[u][i]; if (dep[e.to] == dep[u] + 1 && e.cap > 0 ) { int d = dfs (e.to, min (limit, e.cap)); if (d > 0 ) { e.cap -= d; G[e.to][e.rev].cap += d; return d; } } } return 0 ; } int dinic () { int flow = 0 ; while (bfs ()) { memset (cur, 0 , sizeof cur); while (int d = dfs (S, INF)) { flow += d; } } return flow; } void solve () { if (!(cin >> n >> m >> S >> T)) return ; for (int i = 1 ; i <= n; i++) G[i].clear (); for (int i = 0 ; i < m; i++) { int u, v, w; cin >> u >> v >> w; if (u == v) continue ; add_edge (u, v, w); } int max_flow = dinic (); cout << max_flow << endl; } signed main () { ios::sync_with_stdio (false ); cin.tie (0 ); int t; if (!(cin >> t)) return 0 ; while (t--) { solve (); } return 0 ; }
最大二分匹配 dinic找最大二分匹配 时间复杂度(一般图) :$O(V^2 E)$时间复杂度(二分图/单位容量网络) :$O(E\sqrt{V})$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 #include <bits/stdc++.h> #define int long long using namespace std;const int N = 10010 , M = 200010 , INF = 1e15 ;struct edge { int ed, len, id; }; vector<edge> e[N]; int n, m, S, T;int dep[N], cur[N];bool bfs () { memset (dep, -1 , sizeof dep); queue<int > q; q.push (S); dep[S] = 0 ; while (!q.empty ()) { int t = q.front (); q.pop (); for (int i = 0 ; i < e[t].size (); i++) { int ed = e[t][i].ed; if (dep[ed] == -1 && e[t][i].len) { dep[ed] = dep[t] + 1 ; q.push (ed); } } } memset (cur, 0 , sizeof (cur)); return (dep[T] != -1 ); } int dfs (int st, int limit) { if (st == T) return limit; for (int i = cur[st]; i < e[st].size (); i++) { cur[st] = i; int ed = e[st][i].ed; if (dep[ed] == dep[st] + 1 && e[st][i].len) { int t = dfs (ed, min (e[st][i].len, limit)); if (t) { e[st][i].len -= t; e[ed][e[st][i].id].len += t; return t; } else dep[ed] = -1 ; } } return 0 ; } int dinic () { int r = 0 , flow; while (bfs ()) while (flow = dfs (S, INF)) r += flow; return r; } void add_edge (int u, int v, int cap) { int ui = e[u].size (); int vi = e[v].size (); e[u].push_back ({v, cap, vi}); e[v].push_back ({u, 0 , ui}); } signed main () { ios::sync_with_stdio (0 ); cin.tie (0 ); int nl, nr, m_edges; while (cin >> nl >> nr >> m_edges) { for (int i = 0 ; i <= nl + nr + 2 ; ++i) e[i].clear (); S = 0 , T = nl + nr + 1 ; for (int i = 1 ; i <= nl; i++) add_edge (S, i, 1 ); for (int i = 1 ; i <= nr; i++) add_edge (nl + i, T, 1 ); while (m_edges--) { int u, v; cin >> u >> v; if (u > nl || v > nr) continue ; add_edge (u, nl + v, 1 ); } cout << dinic () << '\n' ; } return 0 ; }
匈牙利算法 $O(V \cdot E)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <vector> #include <algorithm> #include <cstring> using namespace std;const int MAXN = 405 ; int n;long long a[MAXN], p_req[MAXN];long long b[MAXN], q_req[MAXN];vector<int > G[MAXN]; int matchR[MAXN]; bool seen[MAXN]; bool dfs (int u) { for (int v : G[u]) { if (seen[v]) continue ; seen[v] = true ; if (matchR[v] == 0 || dfs (matchR[v])) { matchR[v] = u; return true ; } } return false ; } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cin >> n; for (int i = 1 ; i <= n; ++i) cin >> a[i]; for (int i = 1 ; i <= n; ++i) cin >> p_req[i]; for (int i = 1 ; i <= n; ++i) cin >> b[i]; for (int i = 1 ; i <= n; ++i) cin >> q_req[i]; for (int i = 1 ; i <= n; ++i) { for (int j = 1 ; j <= n; ++j) { if (b[j] >= p_req[i] && a[i] >= q_req[j]) { G[i].push_back (j); } } } int result = 0 ; for (int u = 1 ; u <= n; ++u) { memset (seen, 0 , sizeof (seen)); if (dfs (u)) ++result; } cout << result << "\n" ; return 0 ; }
计算几何,叉积,相交 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <iostream> #include <algorithm> using namespace std;struct Point { long long x, y; }; struct Segment { Point p1, p2; }; long long cross (Point &p1, Point &p2, Point &p3) { return (p2. x - p1. x) * (p3. y - p1. y) - (p2. y - p1. y) * (p3. x - p1. x); } int sign (long long x) { return (x > 0 ) - (x < 0 ); } bool onSegment (Point &p1, Point &p2, Point &p3) { return (p3. x >= min (p1. x, p2. x)) && (p3. x <= max (p1. x, p2. x)) && (p3. y >= min (p1. y, p2. y)) && (p3. y <= max (p1. y, p2. y)); } bool intersect (Segment &s1, Segment &s2) { Point A = s1. p1, B = s1. p2; Point C = s2. p1, D = s2. p2; long long c1 = cross (A, B, C); long long c2 = cross (A, B, D); long long c3 = cross (C, D, A); long long c4 = cross (C, D, B); int s1_sign = sign (c1); int s2_sign = sign (c2); int s3_sign = sign (c3); int s4_sign = sign (c4); if (s1_sign * s2_sign < 0 && s3_sign * s4_sign < 0 ) return true ; if (c1 == 0 && onSegment (A, B, C)) return true ; if (c2 == 0 && onSegment (A, B, D)) return true ; if (c3 == 0 && onSegment (C, D, A)) return true ; if (c4 == 0 && onSegment (C, D, B)) return true ; return false ; } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int T; cin >> T; while (T--) { long long x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; Segment s1 = {{x1, y1}, {x2, y2}}; cin >> x1 >> y1 >> x2 >> y2; Segment s2 = {{x1, y1}, {x2, y2}}; cout << (intersect (s1, s2) ? "Yes" : "No" ) << '\n' ; } }
凸包 Graham:$O(nlgn)$ ,另外一种Jarvis$O(nh)$(h为凸包上顶点的个数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <vector> #include <algorithm> using namespace std;struct Point { int x, y; }; int cross (const Point &p1, const Point &p2, const Point &p3) { return (p2. x - p1. x) * (p3. y - p1. y) - (p2. y - p1. y) * (p3. x - p1. x); } Point base; bool cmp (const Point &a, const Point &b) { int c = cross (base, a, b); if (c == 0 ){ int da = (a.x - base.x)*(a.x - base.x) + (a.y - base.y)*(a.y - base.y); int db = (b.x - base.x)*(b.x - base.x) + (b.y - base.y)*(b.y - base.y); return da < db; } return c > 0 ; } vector<Point> grahamScan (vector<Point> &points) { int n = points.size (); if (n <= 1 ) return points; int idx = 0 ; for (int i = 1 ; i < n; i++){ if (points[i].y < points[idx].y || (points[i].y == points[idx].y && points[i].x < points[idx].x)){ idx = i; } } swap (points[0 ], points[idx]); base = points[0 ]; sort (points.begin () + 1 , points.end (), cmp); vector<Point> hull; hull.push_back (points[0 ]); hull.push_back (points[1 ]); for (int i = 2 ; i < n; i++){ while (hull.size () > 1 && cross (hull[hull.size () - 2 ], hull.back (), points[i]) <= 0 ){ hull.pop_back (); } hull.push_back (points[i]); } return hull; }
FFT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 #include <iostream> #include <complex> #include <vector> #include <cmath> using namespace std;typedef complex<double > Complex;const double pi = acos (-1.0 );vector<Complex> recursive_fft (const vector<Complex>& a, bool invert) { int n = a.size (); if (n == 1 ) return a; Complex wn (cos(2 * pi / n * (invert == true ? -1 : 1 )), sin(2 * pi / n * (invert == true ? -1 : 1 ))) ; Complex w (1 , 0 ) ; vector<Complex> a0 (n / 2 ) , a1 (n / 2 ) ; for (int i = 0 ; i < n / 2 ; i++){ a0[i] = a[2 * i]; a1[i] = a[2 * i + 1 ]; } vector<Complex> y0 = recursive_fft (a0, invert); vector<Complex> y1 = recursive_fft (a1, invert); vector<Complex> y (n) ; for (int k = 0 ; k < n / 2 ; k++){ Complex u = y0[k]; Complex t = w * y1[k]; y[k] = u + t; y[k + n / 2 ] = u - t; w = w * wn; } return y; } vector<Complex> recursive_fft_whole (const vector<Complex>& a, bool invert) { vector<Complex> y = recursive_fft (a, invert); int n = y.size (); if (invert == true ){ for (int i = 0 ; i < n; i++){ y[i] /= n; } } return y; } unsigned int rev_x (unsigned int k, int bit_len) { unsigned int y = 0 ; unsigned int bit; for (int i = 0 ; i < bit_len; i++){ bit = (k >> i) & 1 ; y |= (bit << (bit_len - 1 - i)); } return y; } vector<Complex> bit_reverse_copy (const vector<Complex>& a) { int n = a.size (); int log_n = 0 ; while ((1 << log_n) < n) log_n++; vector<Complex> A (n) ; for (int k = 0 ; k < n; k++){ int reversed_k = rev_x (k, log_n); A[k] = a[reversed_k]; } return A; } vector<Complex> iterative_fft (vector<Complex> &a, bool invert) { vector<Complex> A = bit_reverse_copy (a); int n = A.size (); int log_n = 0 ; while ((1 << log_n) < n) log_n++; for (int s = 1 ; s <= log_n; s++){ int m = 1 << s; double ang = 2 * pi / m * ((invert == true ) ? -1 : 1 ); Complex wm (cos(ang), sin(ang)) ; for (int k = 0 ; k < n; k+=m){ Complex w (1.0 , 0.0 ) ; for (int j = 0 ; j < m / 2 ; j++){ Complex t = w * A[k + j + m / 2 ]; Complex u = A[k + j]; A[k + j] = u + t; A[k + j + m / 2 ] = u - t; w = w * wm; } } } if (invert) { for (int i = 0 ; i < n; i++) { A[i] /= n; } } return A; } int main () { vector<Complex> a = {0 , 1 , 2 ,3 , 5 }; int original_n = a.size (); int n = 1 ; while (n < a.size ()) n <<= 1 ; a.resize (n); vector<Complex> y = iterative_fft (a, false ); for (int i = 0 ; i < y.size (); i++) { double real = abs (y[i].real ()) < 1e-10 ? 0 : y[i].real (); double imag = abs (y[i].imag ()) < 1e-10 ? 0 : y[i].imag (); cout << "y[" << i << "] = " << real << " + " << imag << "i" << endl; } }
多项式相乘 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 #include <iostream> #include <complex> #include <vector> #include <cmath> using namespace std;typedef complex<double > Complex;const double pi = acos (-1.0 );%% vector<Complex> recursive_fft (const vector<Complex>& a, bool invert) { int n = a.size (); if (n == 1 ) return a; Complex wn (cos(2 * pi / n * (invert == true ? -1 : 1 )), sin(2 * pi / n * (invert == true ? -1 : 1 ))) ; Complex w (1 , 0 ) ; vector<Complex> a0 (n / 2 ) , a1 (n / 2 ) ; for (int i = 0 ; i < n / 2 ; i++){ a0[i] = a[2 * i]; a1[i] = a[2 * i + 1 ]; } vector<Complex> y0 = recursive_fft (a0, invert); vector<Complex> y1 = recursive_fft (a1, invert); vector<Complex> y (n) ; for (int k = 0 ; k < n / 2 ; k++){ Complex u = y0[k]; Complex t = w * y1[k]; y[k] = u + t; y[k + n / 2 ] = u - t; w = w * wn; } return y; } vector<Complex> recursive_fft_whole (const vector<Complex>& a, bool invert) { vector<Complex> y = recursive_fft (a, invert); int n = y.size (); if (invert == true ){ for (int i = 0 ; i < n; i++){ y[i] /= n; } } return y; } %% unsigned int rev_x (unsigned int k, int bit_len) { unsigned int y = 0 ; unsigned int bit; for (int i = 0 ; i < bit_len; i++){ bit = (k >> i) & 1 ; y |= (bit << (bit_len - 1 - i)); } return y; } vector<Complex> bit_reverse_copy (const vector<Complex>& a) { int n = a.size (); int log_n = 0 ; while ((1 << log_n) < n) log_n++; vector<Complex> A (n) ; for (int k = 0 ; k < n; k++){ int reversed_k = rev_x (k, log_n); A[k] = a[reversed_k]; } return A; } vector<Complex> iterative_fft (vector<Complex> &a, bool invert) { vector<Complex> A = bit_reverse_copy (a); int n = A.size (); int log_n = 0 ; while ((1 << log_n) < n) log_n++; for (int s = 1 ; s <= log_n; s++){ int m = 1 << s; double ang = 2 * pi / m * ((invert == true ) ? -1 : 1 ); Complex wm (cos(ang), sin(ang)) ; for (int k = 0 ; k < n; k+=m){ Complex w (1.0 , 0.0 ) ; for (int j = 0 ; j < m / 2 ; j++){ Complex t = w * A[k + j + m / 2 ]; Complex u = A[k + j]; A[k + j] = u + t; A[k + j + m / 2 ] = u - t; w = w * wm; } } } if (invert) { for (int i = 0 ; i < n; i++) { A[i] /= n; } } return A; } vector<Complex> multiply (const vector<Complex>&a, const vector<Complex>& b) { int n = a.size (); vector<Complex> res (n) ; for (int i = 0 ; i < n; i++){ res[i] = a[i] * b[i]; } return res; } int main () { vector<Complex> a = {9 , 1 , 2 , 3 }; vector<Complex> b = {2 , 3 , 4 }; int origin_n = a.size () + b.size () - 1 ; int n = 1 ; while (n < origin_n) n <<= 1 ; a.resize (n); b.resize (n); vector<Complex> u = iterative_fft (a, false ); vector<Complex> v = iterative_fft (b, false ); vector<Complex> res = multiply (u, v); res = iterative_fft (res, true ); for (int i = 0 ; i < origin_n; i++) { double real = abs (res[i].real ()) < 1e-10 ? 0 : res[i].real (); double imag = abs (res[i].imag ()) < 1e-10 ? 0 : res[i].imag (); cout << "y[" << i << "] = " << real << " + " << imag << "i" << endl; } }
拓展欧几里得 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> #include <algorithm> #include <cmath> #include <vector> using namespace std;int Exgcd (int a, int b, int * x, int *y) { if (b == 0 ){ *x = 1 ; *y = 0 ; return a; }else { int xx, yy; int d = Exgcd (b, a % b, &xx, &yy); *x = yy; *y = xx - (a / b) * yy; return d; } } int main () { int a = 15 ; int b = 21 ; int c = 0 ; int cc = 0 ; int * x = &c; int * y = &cc; int d = Exgcd (a, b, x, y); cout << d << ' ' << *x << ' ' << *y; }
平面最近点对 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include <stdio.h> #include <stdlib.h> #include <math.h> typedef struct { double x; double y; }point; point p[500005 ],temp[500005 ],buf[500005 ]; double dist (point a, point b) { return sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } int cmp_x (const void *a, const void *b) { point *p1 = (point *)a; point *p2 = (point *)b; if (p1 -> x > p2 -> x) return 1 ; return -1 ; } double solve (int l, int r) { if (r - l <= 2 ){ double min = 1e7 ; for (int i = l; i <= r; i++){ for (int j = i + 1 ; j <= r; j++){ if (dist (p[i], p[j]) < min) min = dist (p[i], p[j]); } } for (int i = l + 1 ; i <= r; i++){ point key = p[i]; int j = i - 1 ; while (j >= l && p[j].y > key.y){ p[j + 1 ] = p[j]; j--; } p[j + 1 ] = key; } return min; } int mid = (l + r) / 2 ; double midx = p[mid].x; double d = fmin (solve (l, mid), solve (mid + 1 , r)); int i = l,j = mid + 1 , k = l; while (i <= mid && j <= r){ if (p[i].y <= p[j].y) temp[k++] = p[i++]; else temp[k++] = p[j++]; } while (i <= mid) temp[k++] = p[i++]; while (j <= r) temp[k++] = p[j++]; for (i = l; i <= r; i++) p[i] = temp[i]; int sz = 0 ; for (int i = l; i <= r; i++){ if (fabs (p[i].x - midx) < d){ buf[sz++] = p[i]; } } for (int i = 0 ; i < sz; i++){ for (j = i + 1 ; j <= i + 7 && j < sz; j++){ double t = dist (buf[i], buf[j]); if (t < d) d = t; } } return d; } int main () { int n; scanf ("%d" , &n); for (int i = 0 ; i < n; i++){ scanf ("%lf%lf" , &p[i].x, &p[i].y); } qsort (p, n, sizeof (p[0 ]), cmp_x); double ans = solve (0 ,n - 1 ); printf ("%.4f" , ans); return 0 ; }
KMP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <iostream> #include <vector> #include <string> using namespace std;vector<int > computeNext (const string& P) { int m = P.length (); vector<int > next (m) ; next[0 ] = 0 ; for (int i = 1 , j = 0 ; i < m; i++) { while (j > 0 && P[i] != P[j]) { j = next[j - 1 ]; } if (P[i] == P[j]) { j++; } next[i] = j; } return next; } void KMP (const string& T, const string& P) { int n = T.length (); int m = P.length (); if (m == 0 ) return ; vector<int > next = computeNext (P); for (int i = 0 , j = 0 ; i < n; i++) { while (j > 0 && T[i] != P[j]) { j = next[j - 1 ]; } if (T[i] == P[j]) { j++; } if (j == m) { cout << "在位移 " << i - m + 1 << " 处找到匹配" << endl; j = next[j - 1 ]; } } }
PA算法字符串找子串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <iostream> #include <vector> #include <string> using namespace std;vector<vector<int >> computeTransitionTableFast (const string& P) { int m = P.length (); int alphabetSize = 256 ; vector<vector<int >> delta (m + 1 , vector <int >(alphabetSize, 0 )); if (m > 0 ) { delta[0 ][(unsigned char )P[0 ]] = 1 ; } int restartState = 0 ; for (int q = 1 ; q < m; q++) { for (int a = 0 ; a < alphabetSize; a++) { delta[q][a] = delta[restartState][a]; } delta[q][(unsigned char )P[q]] = q + 1 ; restartState = delta[restartState][(unsigned char )P[q]]; } for (int a = 0 ; a < alphabetSize; a++) { delta[m][a] = delta[restartState][a]; } return delta; } void search (const string& T, const string& P) { int n = T.length (); int m = P.length (); if (m == 0 ) return ; vector<vector<int >> delta = computeTransitionTableFast (P); int q = 0 ; for (int i = 0 ; i < n; i++) { q = delta[q][(unsigned char )T[i]]; if (q == m) { cout << "find,index: " << i - m + 1 << endl; } } } int main () { string text = "abacababacaababacaababacaababaca" ; string pattern = "ababaca" ; cout << "text: " << text << endl; cout << "pattern: " << pattern << endl; search (text, pattern); return 0 ; }
24级考试题(回忆版 第一道诚信题
第二道选择题 有一个算时间复杂度,$T(n)=T(\frac{n}{2})+T(\frac{n}{4})+n$ 有一个问KMP预处理过程和查找过程各自的时间复杂度 有一个问是不是BFS能求出所有权值为1的图的最短路径,但是DFS不能求出权值任意的点的最短路径 有一个问graham算法中以下代码片段的时间复杂度
1 2 3 4 5 6 7 for (int i = 2 ; i < n; i++){ while (hull.size () > 1 && cross (hull[hull.size () - 2 ], hull.back (), points[i]) <= 0 ){ hull.pop_back (); } hull.push_back (points[i]); } return hull;
可能还有但是忘了
第三题是KMP板子,一模一样(再次强调板子正确的重要性
第四题是输入T个n个节点m条边的无向图,判断它是不是树(n个点,n-1条边并且联通)
第五题是贪心(n个人,输入每个人无奖、获A奖、获B奖的快乐值,你只有一个A奖和一个B奖,问总快乐值最大是多少
第六题是kfc逆序对(和上面板子的K重逆序对几乎一模一样,只需要把(long long)a[i] > (long long)kk * a[j]改成题干给的条件就行了
第七题是逆天凸包题,给n个点,求出这群点的凸包,再把凸包上的点去掉,通过若干次这样的操作 问多少次才会把所有的点都去掉,最后一次去掉了多少个点,并且输出最后一次去掉的点的坐标(按照题目输入的顺序)(这破题我敲了四十分钟怒拿0分
第八题是动态规划,忘了,反正不会做,直接输出No可以混0.3分
第九题是最短路径。给n个点,m条边,每条边有自己的权值和一个快乐值,你可以让任意一条路径上快乐值最大的一条边的权值变成0,问最短路径的总权值是多少。
第十题没看,据说是FFT