├── 0_竞赛介绍 ├── ACM竞赛技巧.md ├── CCF CSP比赛技巧.md ├── README.md ├── 信息学竞赛技巧.md └── 蓝桥杯比赛技巧.md ├── 10_suffixarray ├── 1_suffixarray.md ├── code │ └── 1_suffixarray.cpp └── content.md ├── 11_expert_datastructure ├── 1_segment_tree_point.md ├── 2_segment_tree_interval.md ├── 3_binary_index_tree.md ├── 4_2d_binary_index_tree.md ├── 5_discretization.md ├── code │ ├── 1_segment_tree_point.cpp │ ├── 2_segment_tree_interval.cpp │ ├── 3_binary_index_tree.cpp │ ├── 4_2d_binary_index_tree.cpp │ └── 5_discretization.cpp └── content.md ├── 1_datastructure ├── 1_vector.md ├── 2_set.md ├── 3_map.md ├── 4_stack.md ├── 5_queue.md ├── 6_priority_queue.md ├── 7_disjoint.md ├── code │ ├── 1_vector.cpp │ ├── 2_set.cpp │ ├── 3_map.cpp │ ├── 4_stack.cpp │ ├── 5_queue.cpp │ ├── 6_priority_queue.cpp │ └── 7_disjoint.cpp ├── content.md ├── 优先队列.cpp ├── 优先队列.md ├── 单调队列.cpp ├── 单调队列.md ├── 双端队列.cpp ├── 双端队列.md ├── 队列.cpp └── 队列.md ├── 2_search ├── 1_queen.md ├── 2_bfs.md ├── code │ ├── 1_queen.cpp │ └── 2_bfs.cpp └── content.md ├── 3_dp ├── 1_knapsack.md ├── 2_LIS.md ├── 3_LCS.md ├── 4_stage.md ├── code │ ├── 1_knapsack.cpp │ ├── 2_LIS.cpp │ ├── 3_LCS.cpp │ └── 4_stage.cpp └── content.md ├── 4_math ├── 1_gcd.md ├── 2_prime.md ├── 3_euler.md ├── 4_exgcd.md ├── 5_binary_pow.md ├── 6_matrix_pow.md ├── code │ ├── 1_gcd.cpp │ ├── 2_prime.cpp │ ├── 3_euler.cpp │ ├── 4_exgcd.cpp │ ├── 5_binary_pow.cpp │ └── 6_matrix_pow.cpp └── content.md ├── 5_tree_grpah ├── 1_adjacency_list.md ├── 2_graph_dfs.md ├── 3_graph_bfs.md ├── code │ ├── 1_adjacency_list.cpp │ ├── 2_graph_dfs.cpp │ └── 3_graph_bfs.cpp └── content.md ├── 6_short_path ├── 1_dij.md ├── 2_spfa.md ├── 3_floyd.md ├── code │ ├── 1_dij.cpp │ ├── 2_spfa.cpp │ └── 3_floyd.cpp └── content.md ├── 7_graph_algorithm ├── 1_kruskal.md ├── 2_lca.md ├── 3_topsort.md ├── 4_tarjan.md ├── code │ ├── 1_kruskal.cpp │ ├── 2_lca.cpp │ ├── 3_topsort.cpp │ └── 4_tarjan.cpp └── content.md ├── 8_match_and_flow ├── 1_hungury.md ├── 2_dinic.md ├── 3_cost_flow.md ├── code │ ├── 1_hungury.cpp │ ├── 2_dinic.cpp │ └── 3_cost_flow.cpp └── content.md ├── 9_string_basic ├── 1_kmp.md ├── 2_exkmp.md ├── 3_trie.md ├── 4_ac.md ├── code │ ├── 1_kmp.cpp │ ├── 2_exkmp.cpp │ ├── 3_trie.cpp │ └── 4_ac.cpp └── content.md └── README.md /0_竞赛介绍/ACM竞赛技巧.md: -------------------------------------------------------------------------------- 1 | # `ACM竞赛技巧` 2 | 3 | 本文主要讲一下弱校如何起步以及训练的问题 4 | - 首先要明确一个问题,大学生和高中生是不一样的,大学里的诱惑实在太多,绝大多数人都没办法一直坚持。弱校学生搞ACM更多的是想每年有几次公费出去旅游的机会罢了。 5 | - ACM竞赛和信息学竞赛不同,ACM竞赛是三个人一队的,所以如何选择另外两个队友将会直接影响到你努力的结果。 6 | - 如果你在一个弱校,但你又想在大学期间得到比较好的成绩,那么你一定要记住打死都不要去管队友的水平,自己专心训练就可以了。就算比赛的时候带两个翻译,也比1 + 1 + 1 < 1来的好。当然如果不是那么在乎成绩,那当然带着队友一起划水,一起围观神犇,一起翘课打游戏也是美滋滋的大学生活。 7 | 8 | 9 | 当你看这篇文章的时候,我假定你是一个为了省赛拿奖而努力的初学者 10 | - 在ACM竞赛中,一般会给出至少一道签到题以及一两道简单题,如何快速且一次AC简单题目将是省赛拿奖的关键。 11 | - 很多ACM比赛,题目出的区分度都不是太好,偶尔有的比赛甚至同样的题数,做的快的金牌,做的慢的铁牌,这对初学者来说是相当有利。 12 | - 初学阶段千万不要想着尽量多的学算法,而是要把代码能力练好,多做基础算法和复杂的模拟,要做到程序随心而动。不要程序写出来,连自己都没有把握写对没有。 13 | - 还是那个问题,一般情况下出题人只要没吃错药是不会出模板题的,但是还真不好说,现在的出题人都喜欢愚乐选手。这个时候你的两个划水的队友就到了体现作用的时候了,忽悠他们去整理各类题的模板,学一下怎么套模板就行了。 14 | 15 | 如果你通过基础算法侥幸拿到了省赛三等或二等,那么你的下一步目标就是拿到区域赛现场名额 16 | - 一般比较稳定的现场赛名额获取方式是通过各个赛区的网络赛,但是想进入学校排名前100甚至前90,对于一个acm刚刚起步的学校,这几乎是不可能的。因为你要面对的不仅是传统强校,别人acm发展的久的学校,退役的队员也会帮忙打,还有一些学校会请高中生帮忙打,至于关系好的学校互相探讨就是再正常不过的现象了。 17 | - 不用担心,虽然网络赛很难拿到名额,但是你可以在暑假的时候去参加邀请赛,一般来说,都能拿到一个现场赛的名额。 18 | - 最后如果你省赛、邀请赛、网络赛都没有能够拿到名额,这时候就需要你真(表)情(演)流(演)露(技)了,找一个文采好的同学,写一份声请并茂的弱校名额申请。一般举办方都会留一些名额给那些努力型选手的。至于怎么写,比如平时训练刻苦风雨无阻啊,暑期集训30天90包泡面啊之类的博取举办方的同情。 19 | 20 | 如何系统性的训练 21 | - 如果周围有acm强校,一定要厚着脸皮去蹭课旁听,特别是暑期集训,千万不能错过。 22 | - 如果是小城市,那么建议跟着本书学习,一步一个脚印,最好找两个队友一起讨论。如果书中有任何错误,欢迎及时指正。 23 | 24 | > 切记:学算法切勿心浮气躁,如果感觉状态不好,可以先调整一下状态再学习。 25 | -------------------------------------------------------------------------------- /0_竞赛介绍/CCF CSP比赛技巧.md: -------------------------------------------------------------------------------- 1 | # `CCF CSP比赛技巧` 2 | 3 | 其实这个没什么好说的,主要记住以下几点 4 | - 做题仔细,最好写一遍暴力对拍 5 | - 注意取舍,特别是最后一题,没必要死磕全部数据的算法,第一不一定是满分 6 | - 每年都有几次的比赛,放平心态,考虑如何得分最大化 7 | - 不要去写自己平时没写过的算法 8 | -------------------------------------------------------------------------------- /0_竞赛介绍/README.md: -------------------------------------------------------------------------------- 1 | # `竞赛介绍` 2 | 3 | - 本书主要围绕中学NOIP/NOI以及大学ACM/ICPC竞赛进行展开 4 | - 本书使用`C & C++-STL`混合编程 5 | - 本书难度不分先后 6 | - 本书旨在提供一个系统性的学习训练指南,内容会很多,帮助初学选手在脑海中建立一个竞赛算法的整体框架。 7 | - 本书亦可作为未起步或刚起步的教练指导参考书 8 | -------------------------------------------------------------------------------- /0_竞赛介绍/信息学竞赛技巧.md: -------------------------------------------------------------------------------- 1 | # `信息学竞赛技巧` 2 | 3 | 4 | - 中学的信息学竞赛,绝大多数同学都是抱着功利性的目的来搞竞赛的。 5 | - 当然,这没有任何问题,人都是有欲望才有前进的动力,如果大家都无欲无求,剃发出家为尼为僧,社会还怎么发展进步呢? 6 | - 有的人只学了半年却比你学了一年甚至两年的成绩更好,真的完全是天赋的原因吗?或许不见得,也许是你平时的学习方法和比赛技巧出了问题。 7 | 8 | 9 | 当你看这篇文章的时候,我假定你是一个为了省一在奋斗的初学者 10 | - 对于大多数弱省而言,省一只需要会一个算法就行了,那就是暴力。 11 | - 暴力的核心是搜索,如果你想要在最短的时间内取得不错的成绩,那么就请摒弃那些纷繁复杂的算法专心学好搜索,如何找到更好的建模方式,如何剪枝,如何记忆化,如何贪心水过更多的数据将是你学习的重点。 12 | - NOIP中一般有两道送分题,或许是一个找规律,或许是一个模拟,或许仅仅是签到。遇到动态规划,直接搜,遇到数据结构,直接搜,遇到图论,直接搜,遇到数学,先搜再找规律。 13 | - 很多初学的竞赛选手有一个学习误区,就是想要快速的把所有算法都学一遍。但是就算你背会了所有的算法模板又如何呢?去赌今年的出题人会不会吃错药,出一个模板题让大家乐呵乐呵? 14 | 15 | 如果你通过暴力侥幸拿到了省一,那么你的下一步目标就是省选 16 | - 在大多数省份,省一末尾和省队之间的实力差距或许比不会算法和省一之间的差距更大。 17 | - 意识到差距之后,就不应该再想着用暴力来解决一切问题,但也不要急急忙忙的看到算法就学。首先应该静下心思考一下,也许学习一年,很大可能依然不能达到省队的水平。问一问自己是否有足够的时间,比如你现在才高一,下一次就算还是省一,你还有机会在高三翻盘。如果是这样,你就应该慢慢的扎实的学习,不要图进度。要知道看会十个算法,不如你掌握一个算法。反之,你已经高二了,你就应该考虑一下后路,而不是在竞赛上孤注一掷。进入大学之后,搞ACM竞赛你就已经赢在了起跑线上,你依然可能翻盘,毕竟4年闲暇的大学时光足够你做许多事。 18 | 19 | 如何系统性的训练 20 | - 首先应该意识到正规军和杂牌军的区别,其实可以简单的理解为省里的重点中学和山区里的学校之间的区别。 21 | - 意识到区别之后就应该想方设法地去正规军那学习或者旁听,不要嫌距离远就在网上听课甚至自己瞎琢磨,历史的经验告诉我们闭门造车离失败就不远了。 22 | - 网上搜一下附近有没有大学的ACM集训队,如果有就去找他们队长联系,跟着一起训练,有不会的就厚着脸皮问。在我看来,大学搞ACM的大多都是乐于助人的,也许ACM还有修身养性的功效吧。 23 | - 回归正题,系统性的训练分为两类,一是算法知识的不断积累,二是比赛状态的不断提升。刚开始不要盲目的去打比赛,既浪费时间又打击自信,静下心来,按照本书的算法内容不断学习,做了足够的积累之后再去打比赛查漏补缺,开阔眼界。 24 | 25 | > 切记:学算法切勿心浮气躁,如果感觉状态不好,可以先调整一下状态再学习。 26 | -------------------------------------------------------------------------------- /0_竞赛介绍/蓝桥杯比赛技巧.md: -------------------------------------------------------------------------------- 1 | # `蓝桥杯比赛技巧` 2 | 3 | 这个也没什么好说的,自己研究历年真题去。 4 | -------------------------------------------------------------------------------- /10_suffixarray/code/1_suffixarray.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | const int MAX_N = 210000; 5 | char s[MAX_N]; 6 | int sa[MAX_N], t[MAX_N], t2[MAX_N], c[MAX_N], n; 7 | void build_sa(int m) 8 | { 9 | int i, *x = t, *y = t2; 10 | for (i = 0; i < m; i++) c[i] = 0; 11 | for (i = 0; i < n; i++) c[x[i] = s[i]]++; 12 | for (i = 1; i < m; i++) c[i] += c[i - 1]; 13 | for (i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; 14 | for (int k = 1; k <= n; k <<= 1) { 15 | int p = 0; 16 | for (i = n - k; i < n; i++) y[p++] = i; 17 | for (i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k; 18 | 19 | for (i = 0; i < m; i++) c[i] = 0; 20 | for (i = 0; i < n; i++) c[x[y[i]]]++; 21 | for (i = 1; i < m; i++) c[i] += c[i - 1]; 22 | for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; 23 | swap(x, y); 24 | p = 1; 25 | x[sa[0]] = 0; 26 | for (i = 1; i < n; i++) x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++; 27 | if (p >= n) 28 | break; 29 | m = p; 30 | } 31 | } 32 | int m; 33 | int cmp_suffix(char* patter, int p) { 34 | return strncmp(patter, s + sa[p], m); 35 | } 36 | int find(char* P) { 37 | m = strlen(P); 38 | if (cmp_suffix(P, 0) < 0) return -1; 39 | if (cmp_suffix(P, n - 1) > 0) return -1; 40 | int l = 0, r = n; 41 | while (l < r) { 42 | int mid = (l + r) >> 1; 43 | int res = cmp_suffix(P, mid); 44 | if (!res) { 45 | return sa[mid]; 46 | } 47 | if (res < 0) { 48 | r = mid; 49 | } else { 50 | l = mid + 1; 51 | } 52 | } 53 | return -1; 54 | } 55 | int Rank[MAX_N], h[MAX_N]; 56 | void get_h() { 57 | int i, j, k = 0; 58 | for (i = 0; i < n; i++) Rank[sa[i]] = i; 59 | for (i = 0; i < n; i++) { 60 | if (k) k--; 61 | if (Rank[i]) { 62 | j = sa[Rank[i] - 1]; 63 | while (s[i + k] == s[j + k]) k++; 64 | h[Rank[i]] = k; 65 | } 66 | } 67 | } 68 | 69 | int main() { 70 | char ss[MAX_N]; 71 | cin >> s; 72 | n = strlen(s); 73 | build_sa(131); 74 | get_h(); 75 | while (cin >> ss) { 76 | cout << find(ss) << endl; 77 | } 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /10_suffixarray/content.md: -------------------------------------------------------------------------------- 1 | # 后缀数组模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|后缀数组模板|1|| 7 | 8 | 9 | 后缀数组。 10 | -------------------------------------------------------------------------------- /11_expert_datastructure/3_binary_index_tree.md: -------------------------------------------------------------------------------- 1 | # binary_index_tree 的使用 2 | - binary_index_tree 3 | 4 | ### 文件名 5 | binary_index_tree.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 这一节我们学习树状数组(一维)算法。 26 | 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。 27 | 树状数组和线段树很像,但能用树状数组解决的问题,基本上都能用线段树解决,而线段树能解决的树状数组不一定能解决。相比较而言,树状数组效率要高很多。 28 | 我们先定义好要用得数据。在`main`函数上面写下 29 | ```c++ 30 | const int MAX_N = 10010; 31 | int C[MAX_N]; 32 | int n; 33 | ``` 34 | 35 | #### 代码 36 | ```c++ 37 | #include 38 | using namespace std; 39 | const int MAX_N = 10010; 40 | int C[MAX_N]; 41 | int n; 42 | 43 | int main() { 44 | 45 | return 0; 46 | } 47 | ``` 48 | 49 | --- 50 | ### 第二步 51 | #### 讲解 52 | 接下来输入一个数`n`。 53 | 在`main`函数里面写下 54 | ```c++ 55 | cin >> n; 56 | ``` 57 | 58 | #### 提示 59 | ```c++ 60 | #include 61 | using namespace std; 62 | const int MAX_N = 10010; 63 | int C[MAX_N]; 64 | int n; 65 | 66 | int main() { 67 | cin >> n; 68 | 69 | return 0; 70 | } 71 | ``` 72 | 73 | #### 代码 74 | ```c++ 75 | #include 76 | using namespace std; 77 | const int MAX_N = 10010; 78 | int C[MAX_N]; 79 | int n; 80 | 81 | int main() { 82 | cin >> n; 83 | 84 | return 0; 85 | } 86 | ``` 87 | 88 | --- 89 | ### 第三步 90 | #### 讲解 91 | 然后在实现`lowbit(int x)`函数。 92 | 它只保留"从低位向高位数,第一个数字1"作为运算结果。 93 | 这里用到了计算机补码的知识,也可以写成x & ((~x) + 1)。 94 | 在`main`函数里继续写下 95 | ```c++ 96 | int lowbit(int x) { 97 | return x & (-x); 98 | } 99 | ``` 100 | 101 | #### 提示 102 | ```c++ 103 | #include 104 | using namespace std; 105 | const int MAX_N = 10010; 106 | int C[MAX_N]; 107 | int n; 108 | int lowbit(int x) { 109 | return x & (-x); 110 | } 111 | 112 | int main() { 113 | cin >> n; 114 | 115 | return 0; 116 | } 117 | ``` 118 | 119 | 120 | #### 代码 121 | ```c++ 122 | #include 123 | using namespace std; 124 | const int MAX_N = 10010; 125 | int C[MAX_N]; 126 | int n; 127 | int lowbit(int x) { 128 | return x & (-x); 129 | } 130 | 131 | int main() { 132 | cin >> n; 133 | 134 | return 0; 135 | } 136 | ``` 137 | 138 | 139 | --- 140 | ### 第四步 141 | #### 讲解 142 | 接下来实现`getsum(int x)`函数。 143 | 1、令res = 0,转第二步; 144 | 2、假如x <= 0,算法结束,返回res值,否则res = res + C[x],转第三步; 145 | 3、令x = x – lowbit(x),转第二步。 146 | 在`main`函数上方写下 147 | ```c++ 148 | int getsum(int x) { 149 | int res = 0; 150 | for (; x; x -= lowbit(x)) { 151 | res += C[x]; 152 | } 153 | return res; 154 | } 155 | ``` 156 | 157 | #### 提示 158 | ```c++ 159 | #include 160 | using namespace std; 161 | const int MAX_N = 10010; 162 | int C[MAX_N]; 163 | int n; 164 | int lowbit(int x) { 165 | return x & (-x); 166 | } 167 | int getsum(int x) { 168 | int res = 0; 169 | for (; x; x -= lowbit(x)) { 170 | res += C[x]; 171 | } 172 | return res; 173 | } 174 | 175 | int main() { 176 | cin >> n; 177 | 178 | return 0; 179 | } 180 | ``` 181 | 182 | 183 | #### 代码 184 | ```c++ 185 | #include 186 | using namespace std; 187 | const int MAX_N = 10010; 188 | int C[MAX_N]; 189 | int n; 190 | int lowbit(int x) { 191 | return x & (-x); 192 | } 193 | int getsum(int x) { 194 | int res = 0; 195 | for (; x; x -= lowbit(x)) { 196 | res += C[x]; 197 | } 198 | return res; 199 | } 200 | 201 | int main() { 202 | cin >> n; 203 | 204 | return 0; 205 | } 206 | ``` 207 | 208 | 209 | --- 210 | ### 第五步 211 | #### 讲解 212 | 接下来实现`change(int x, int c)`函数。 213 | 1、当x > n时,算法结束,否则转第二步; 214 | 2、C[x] = C[x] + c, x = x + lowbit(x)转第一步。 215 | i = i + lowbit(i)这个过程实际上也只是一个把末尾1补为0的过程。 216 | 在`main`函数上方写下 217 | ```c++ 218 | void change(int x, int c) { 219 | for (; x <= n; x += x & (-x)) { 220 | C[x] += c; 221 | } 222 | } 223 | ``` 224 | 225 | #### 提示 226 | ```c++ 227 | #include 228 | using namespace std; 229 | const int MAX_N = 10010; 230 | int C[MAX_N]; 231 | int n; 232 | int lowbit(int x) { 233 | return x & (-x); 234 | } 235 | int getsum(int x) { 236 | int res = 0; 237 | for (; x; x -= lowbit(x)) { 238 | res += C[x]; 239 | } 240 | return res; 241 | } 242 | void change(int x, int c) { 243 | for (; x <= n; x += x & (-x)) { 244 | C[x] += c; 245 | } 246 | } 247 | 248 | int main() { 249 | cin >> n; 250 | 251 | return 0; 252 | } 253 | ``` 254 | 255 | 256 | #### 代码 257 | ```c++ 258 | #include 259 | using namespace std; 260 | const int MAX_N = 10010; 261 | int C[MAX_N]; 262 | int n; 263 | int lowbit(int x) { 264 | return x & (-x); 265 | } 266 | int getsum(int x) { 267 | int res = 0; 268 | for (; x; x -= lowbit(x)) { 269 | res += C[x]; 270 | } 271 | return res; 272 | } 273 | void change(int x, int c) { 274 | for (; x <= n; x += x & (-x)) { 275 | C[x] += c; 276 | } 277 | } 278 | 279 | int main() { 280 | cin >> n; 281 | 282 | return 0; 283 | } 284 | ``` 285 | 286 | 287 | --- 288 | ### 第六步 289 | #### 讲解 290 | 接下来输入`n`个数并更新到树状数组的区间中。 291 | 在`main`函数里面写下 292 | ```c++ 293 | for (int i = 1; i <= n; ++i) { 294 | int d; 295 | cin >> d; 296 | change(i, d); 297 | } 298 | ``` 299 | 300 | #### 提示 301 | ```c++ 302 | #include 303 | using namespace std; 304 | const int MAX_N = 10010; 305 | int C[MAX_N]; 306 | int n; 307 | int lowbit(int x) { 308 | return x & (-x); 309 | } 310 | int getsum(int x) { 311 | int res = 0; 312 | for (; x; x -= lowbit(x)) { 313 | res += C[x]; 314 | } 315 | return res; 316 | } 317 | void change(int x, int c) { 318 | for (; x <= n; x += x & (-x)) { 319 | C[x] += c; 320 | } 321 | } 322 | 323 | int main() { 324 | cin >> n; 325 | for (int i = 1; i <= n; ++i) { 326 | int d; 327 | cin >> d; 328 | change(i, d); 329 | } 330 | 331 | return 0; 332 | } 333 | ``` 334 | 335 | 336 | #### 代码 337 | ```c++ 338 | #include 339 | using namespace std; 340 | const int MAX_N = 10010; 341 | int C[MAX_N]; 342 | int n; 343 | int lowbit(int x) { 344 | return x & (-x); 345 | } 346 | int getsum(int x) { 347 | int res = 0; 348 | for (; x; x -= lowbit(x)) { 349 | res += C[x]; 350 | } 351 | return res; 352 | } 353 | void change(int x, int c) { 354 | for (; x <= n; x += x & (-x)) { 355 | C[x] += c; 356 | } 357 | } 358 | 359 | int main() { 360 | cin >> n; 361 | for (int i = 1; i <= n; ++i) { 362 | int d; 363 | cin >> d; 364 | change(i, d); 365 | } 366 | 367 | return 0; 368 | } 369 | ``` 370 | 371 | 372 | --- 373 | ### 第七步 374 | #### 讲解 375 | 最后调用`getsum(i)`输出计算的结果。 376 | 在`main`函数里面继续写下 377 | ```c++ 378 | for (int i = 1; i <= n; ++i) { 379 | cout << getsum(i) << " "; 380 | } 381 | ``` 382 | 383 | #### 提示 384 | ```c++ 385 | #include 386 | using namespace std; 387 | const int MAX_N = 10010; 388 | int C[MAX_N]; 389 | int n; 390 | int lowbit(int x) { 391 | return x & (-x); 392 | } 393 | int getsum(int x) { 394 | int res = 0; 395 | for (; x; x -= lowbit(x)) { 396 | res += C[x]; 397 | } 398 | return res; 399 | } 400 | void change(int x, int c) { 401 | for (; x <= n; x += x & (-x)) { 402 | C[x] += c; 403 | } 404 | } 405 | 406 | int main() { 407 | cin >> n; 408 | for (int i = 1; i <= n; ++i) { 409 | int d; 410 | cin >> d; 411 | change(i, d); 412 | } 413 | for (int i = 1; i <= n; ++i) { 414 | cout << getsum(i) << " "; 415 | } 416 | return 0; 417 | } 418 | ``` 419 | 420 | 421 | #### 代码 422 | ```c++ 423 | #include 424 | using namespace std; 425 | const int MAX_N = 10010; 426 | int C[MAX_N]; 427 | int n; 428 | int lowbit(int x) { 429 | return x & (-x); 430 | } 431 | int getsum(int x) { 432 | int res = 0; 433 | for (; x; x -= lowbit(x)) { 434 | res += C[x]; 435 | } 436 | return res; 437 | } 438 | void change(int x, int c) { 439 | for (; x <= n; x += x & (-x)) { 440 | C[x] += c; 441 | } 442 | } 443 | 444 | int main() { 445 | cin >> n; 446 | for (int i = 1; i <= n; ++i) { 447 | int d; 448 | cin >> d; 449 | change(i, d); 450 | } 451 | for (int i = 1; i <= n; ++i) { 452 | cout << getsum(i) << " "; 453 | } 454 | return 0; 455 | } 456 | ``` 457 | 458 | 459 | 460 | --- 461 | ### 完成讲解 462 | 终于完成了,点击运行,看看效果吧。 463 | 464 | 聪明的你一定学会了如何使用`binary_index_tree`了。 465 | -------------------------------------------------------------------------------- /11_expert_datastructure/5_discretization.md: -------------------------------------------------------------------------------- 1 | # discretization 的使用 2 | - discretization 3 | 4 | ### 文件名 5 | discretization.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | int main() { 19 | 20 | return 0; 21 | } 22 | ``` 23 | 24 | --- 25 | ### 第一步 26 | #### 讲解 27 | 这一节我们学习离散化处理。 28 | 先举一个例子说明: 29 | 给定平面上的n个矩形(坐标为整数,矩形与矩形之间可能有重叠的部分),求其覆盖的总面积。平常的想法就是开一个与二维坐标规模相当的二维Boolean数组模拟矩形的“覆盖”(把矩形所在的位置填上True)。可惜这个想法在这里有些问题,因为这个题目中坐标范围相当大(坐标范围为-10^8到10^8之间的整数)。但我们发现,矩形的数量n<=100远远小于坐标范围。每个矩形会在横纵坐标上各“使用”两个值, 100个矩形的坐标也不过用了-10^8到10^8之间的200个值。也就是说,实际有用的值其实只有这么几个。这些值将作为新的坐标值重新划分整个平面,省去中间的若干坐标值没有影响。我们可以将坐标范围“离散化”到1到200之间的数,于是一个200*200的二维数组就足够了。 30 | 实现方法:“排序后处理”对横坐标(或纵坐标)进行一次排序并映射为1到2n的整数,同时记录新坐标的每两个相邻坐标之间在离散化前实际的距离是多少。 31 | 我们先定义好要用的数据。在`main`函数上面写下 32 | ```c++ 33 | const int maxn = 10010; 34 | int num[maxn]; 35 | int temp[maxn]; 36 | int n; 37 | map mp; 38 | ``` 39 | 40 | #### 代码 41 | ```c++ 42 | #include 43 | #include 44 | #include 45 | using namespace std; 46 | const int maxn = 10010; 47 | int num[maxn]; 48 | int temp[maxn]; 49 | int n; 50 | map mp; 51 | int main() { 52 | 53 | return 0; 54 | } 55 | ``` 56 | 57 | --- 58 | ### 第二步 59 | #### 讲解 60 | 接下来输入一个整数`n`,表示总共要输入`n`个数。 61 | 在`main`函数里面写下 62 | ```c++ 63 | cin >> n; 64 | ``` 65 | 66 | #### 提示 67 | ```c++ 68 | #include 69 | #include 70 | #include 71 | using namespace std; 72 | const int maxn = 10010; 73 | int num[maxn]; 74 | int temp[maxn]; 75 | int n; 76 | map mp; 77 | int main() { 78 | cin >> n; 79 | 80 | return 0; 81 | } 82 | ``` 83 | 84 | #### 代码 85 | ```c++ 86 | #include 87 | #include 88 | #include 89 | using namespace std; 90 | const int maxn = 10010; 91 | int num[maxn]; 92 | int temp[maxn]; 93 | int n; 94 | map mp; 95 | int main() { 96 | cin >> n; 97 | 98 | return 0; 99 | } 100 | ``` 101 | 102 | --- 103 | ### 第三步 104 | #### 讲解 105 | 然后输入`n`个数并对应映射到`temp`辅助数组中。 106 | 在`main`函数里继续写下 107 | ```c++ 108 | for (int i = 0; i < n; ++i) { 109 | cin >> num[i]; 110 | temp[i] = num[i]; 111 | } 112 | ``` 113 | 114 | #### 提示 115 | ```c++ 116 | #include 117 | #include 118 | #include 119 | using namespace std; 120 | const int maxn = 10010; 121 | int num[maxn]; 122 | int temp[maxn]; 123 | int n; 124 | map mp; 125 | int main() { 126 | cin >> n; 127 | for (int i = 0; i < n; ++i) { 128 | cin >> num[i]; 129 | temp[i] = num[i]; 130 | } 131 | 132 | return 0; 133 | } 134 | ``` 135 | 136 | 137 | #### 代码 138 | ```c++ 139 | #include 140 | #include 141 | #include 142 | using namespace std; 143 | const int maxn = 10010; 144 | int num[maxn]; 145 | int temp[maxn]; 146 | int n; 147 | map mp; 148 | int main() { 149 | cin >> n; 150 | for (int i = 0; i < n; ++i) { 151 | cin >> num[i]; 152 | temp[i] = num[i]; 153 | } 154 | 155 | return 0; 156 | } 157 | ``` 158 | 159 | 160 | --- 161 | ### 第四步 162 | #### 讲解 163 | 接下来进行排序处理。 164 | 在`main`函数里面继续写下 165 | ```c++ 166 | sort(temp, temp + n); 167 | ``` 168 | 169 | #### 提示 170 | ```c++ 171 | #include 172 | #include 173 | #include 174 | using namespace std; 175 | const int maxn = 10010; 176 | int num[maxn]; 177 | int temp[maxn]; 178 | int n; 179 | map mp; 180 | int main() { 181 | cin >> n; 182 | for (int i = 0; i < n; ++i) { 183 | cin >> num[i]; 184 | temp[i] = num[i]; 185 | } 186 | sort(temp, temp + n); 187 | 188 | return 0; 189 | } 190 | ``` 191 | 192 | 193 | #### 代码 194 | ```c++ 195 | #include 196 | #include 197 | #include 198 | using namespace std; 199 | const int maxn = 10010; 200 | int num[maxn]; 201 | int temp[maxn]; 202 | int n; 203 | map mp; 204 | int main() { 205 | cin >> n; 206 | for (int i = 0; i < n; ++i) { 207 | cin >> num[i]; 208 | temp[i] = num[i]; 209 | } 210 | sort(temp, temp + n); 211 | 212 | return 0; 213 | } 214 | ``` 215 | 216 | 217 | --- 218 | ### 第五步 219 | #### 讲解 220 | 接下来就是去重操作。 221 | `unique`的作用是删除`temp`里相邻且重复的元素。 222 | 在`main`函数里面继续写下 223 | ```c++ 224 | int m = unique(temp, temp + n) - temp; 225 | ``` 226 | 227 | #### 提示 228 | ```c++ 229 | #include 230 | #include 231 | #include 232 | using namespace std; 233 | const int maxn = 10010; 234 | int num[maxn]; 235 | int temp[maxn]; 236 | int n; 237 | map mp; 238 | int main() { 239 | cin >> n; 240 | for (int i = 0; i < n; ++i) { 241 | cin >> num[i]; 242 | temp[i] = num[i]; 243 | } 244 | sort(temp, temp + n); 245 | int m = unique(temp, temp + n) - temp; 246 | 247 | return 0; 248 | } 249 | ``` 250 | 251 | 252 | #### 代码 253 | ```c++ 254 | #include 255 | #include 256 | #include 257 | using namespace std; 258 | const int maxn = 10010; 259 | int num[maxn]; 260 | int temp[maxn]; 261 | int n; 262 | map mp; 263 | int main() { 264 | cin >> n; 265 | for (int i = 0; i < n; ++i) { 266 | cin >> num[i]; 267 | temp[i] = num[i]; 268 | } 269 | sort(temp, temp + n); 270 | int m = unique(temp, temp + n) - temp; 271 | 272 | return 0; 273 | } 274 | ``` 275 | 276 | 277 | --- 278 | ### 第六步 279 | #### 讲解 280 | 接下来我们将剩下的`m`个不重复的元素重新离散化到`mp`容器中。 281 | 在`main`函数里面继续写下 282 | ```c++ 283 | for (int i = 0; i < m; ++i) { 284 | mp[temp[i]] = i + 1; 285 | } 286 | ``` 287 | 288 | #### 提示 289 | ```c++ 290 | #include 291 | #include 292 | #include 293 | using namespace std; 294 | const int maxn = 10010; 295 | int num[maxn]; 296 | int temp[maxn]; 297 | int n; 298 | map mp; 299 | int main() { 300 | cin >> n; 301 | for (int i = 0; i < n; ++i) { 302 | cin >> num[i]; 303 | temp[i] = num[i]; 304 | } 305 | sort(temp, temp + n); 306 | int m = unique(temp, temp + n) - temp; 307 | for (int i = 0; i < m; ++i) { 308 | mp[temp[i]] = i + 1; 309 | } 310 | 311 | return 0; 312 | } 313 | ``` 314 | 315 | 316 | #### 代码 317 | ```c++ 318 | #include 319 | #include 320 | #include 321 | using namespace std; 322 | const int maxn = 10010; 323 | int num[maxn]; 324 | int temp[maxn]; 325 | int n; 326 | map mp; 327 | int main() { 328 | cin >> n; 329 | for (int i = 0; i < n; ++i) { 330 | cin >> num[i]; 331 | temp[i] = num[i]; 332 | } 333 | sort(temp, temp + n); 334 | int m = unique(temp, temp + n) - temp; 335 | for (int i = 0; i < m; ++i) { 336 | mp[temp[i]] = i + 1; 337 | } 338 | 339 | return 0; 340 | } 341 | ``` 342 | 343 | 344 | --- 345 | ### 第七步 346 | #### 讲解 347 | 最后在`main`函数里调用`mp[num[i]]`,输出离散化完成的结果。 348 | 在`main`函数里面继续写下 349 | ```c++ 350 | for (int i = 0; i < n; ++i) { 351 | cout << mp[num[i]] << " "; 352 | } 353 | ``` 354 | 355 | #### 提示 356 | ```c++ 357 | #include 358 | #include 359 | #include 360 | using namespace std; 361 | const int maxn = 10010; 362 | int num[maxn]; 363 | int temp[maxn]; 364 | int n; 365 | map mp; 366 | int main() { 367 | cin >> n; 368 | for (int i = 0; i < n; ++i) { 369 | cin >> num[i]; 370 | temp[i] = num[i]; 371 | } 372 | sort(temp, temp + n); 373 | int m = unique(temp, temp + n) - temp; 374 | for (int i = 0; i < m; ++i) { 375 | mp[temp[i]] = i + 1; 376 | } 377 | for (int i = 0; i < n; ++i) { 378 | cout << mp[num[i]] << " "; 379 | } 380 | return 0; 381 | } 382 | ``` 383 | 384 | 385 | #### 代码 386 | ```c++ 387 | #include 388 | #include 389 | #include 390 | using namespace std; 391 | const int maxn = 10010; 392 | int num[maxn]; 393 | int temp[maxn]; 394 | int n; 395 | map mp; 396 | int main() { 397 | cin >> n; 398 | for (int i = 0; i < n; ++i) { 399 | cin >> num[i]; 400 | temp[i] = num[i]; 401 | } 402 | sort(temp, temp + n); 403 | int m = unique(temp, temp + n) - temp; 404 | for (int i = 0; i < m; ++i) { 405 | mp[temp[i]] = i + 1; 406 | } 407 | for (int i = 0; i < n; ++i) { 408 | cout << mp[num[i]] << " "; 409 | } 410 | return 0; 411 | } 412 | ``` 413 | 414 | 415 | 416 | --- 417 | ### 完成讲解 418 | 终于完成了,点击运行,看看效果吧。 419 | 420 | 聪明的你一定学会了如何使用`discretization`了。 421 | -------------------------------------------------------------------------------- /11_expert_datastructure/code/1_segment_tree_point.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | const int MAX_N = 10010; 4 | int s[4 * MAX_N]; 5 | void up(int p) { 6 | s[p] = s[p * 2] + s[p * 2 + 1]; 7 | } 8 | void modify(int p, int l, int r, int x, int v) { 9 | if (l == r) { 10 | s[p] += v; 11 | return; 12 | } 13 | int mid = (l + r) / 2; 14 | if (x <= mid) { 15 | modify(p * 2, l, mid, x, v); 16 | } else { 17 | modify(p * 2 + 1, mid + 1, r, x, v); 18 | } 19 | up(p); 20 | } 21 | int query(int p, int l, int r, int x, int y) { 22 | if (x <= l && r <= y) { 23 | return s[p]; 24 | } 25 | int mid = (l + r) / 2, res = 0; 26 | if (x <= mid) { 27 | res += query(p * 2, l, mid, x, y); 28 | } 29 | if (y > mid) { 30 | res += query(p * 2 + 1, mid + 1, r, x, y); 31 | } 32 | return res; 33 | } 34 | 35 | int main() { 36 | int n; 37 | cin >> n; 38 | for (int i = 1; i <= n; ++i) { 39 | int d; 40 | cin >> d; 41 | modify(1, 1, n, i, d); 42 | } 43 | int q; 44 | cin >> q; 45 | while (q--) { 46 | int d, x, y; 47 | cin >> d >> x >> y; 48 | if (d == 0) { 49 | modify(1, 1, n, x, y); 50 | } else { 51 | cout << query(1, 1, n, x, y) << endl; 52 | } 53 | } 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /11_expert_datastructure/code/2_segment_tree_interval.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | const int MAX_N = 10010; 4 | int s[4 * MAX_N], col[4 * MAX_N]; 5 | void up(int p) { 6 | s[p] = s[p * 2] + s[p * 2 + 1]; 7 | } 8 | void down(int p, int l, int r) { 9 | if (col[p]) { 10 | int mid = (l + r) / 2; 11 | s[p * 2] += col[p] * (mid - l + 1); 12 | s[p * 2 + 1] += col[p] * (r - mid); 13 | col[p * 2] += col[p]; 14 | col[p * 2 + 1] += col[p]; 15 | col[p] = 0; 16 | } 17 | } 18 | 19 | void modify(int p, int l, int r, int x, int y, int c) { 20 | if (x <= l && r <= y) { 21 | s[p] += (r - l + 1) * c; 22 | col[p] += c; 23 | return; 24 | } 25 | down(p, l, r); 26 | int mid = (l + r) / 2; 27 | if (x <= mid) { 28 | modify(p * 2, l, mid, x, y, c); 29 | } 30 | if (y > mid) { 31 | modify(p * 2 + 1, mid + 1, r, x, y, c); 32 | } 33 | up(p); 34 | } 35 | 36 | int query(int p, int l, int r, int x, int y) { 37 | if (x <= l && r <= y) { 38 | return s[p]; 39 | } 40 | down(p, l, r); 41 | int mid = (l + r) / 2, res = 0; 42 | if (x <= mid) { 43 | res += query(p * 2, l, mid, x, y); 44 | } 45 | if (y > mid) { 46 | res += query(p * 2 + 1, mid + 1, r, x, y); 47 | } 48 | return res; 49 | } 50 | 51 | int main() { 52 | int n; 53 | cin >> n; 54 | for (int i = 1; i <= n; ++i) { 55 | int d; 56 | cin >> d; 57 | modify(1, 1, n, i, i, d); 58 | } 59 | int q; 60 | cin >> q; 61 | while (q--) { 62 | int d, x, y, c; 63 | cin >> d >> x >> y; 64 | if (d == 0) { 65 | cin >> c; 66 | modify(1, 1, n, x, y, c); 67 | } else { 68 | cout << query(1, 1, n, x, y) << endl; 69 | } 70 | } 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /11_expert_datastructure/code/3_binary_index_tree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | const int MAX_N = 10010; 4 | int C[MAX_N]; 5 | int n; 6 | int lowbit(int x) { 7 | return x & (-x); 8 | } 9 | int getsum(int x) { 10 | int res = 0; 11 | for (; x; x -= lowbit(x)) { 12 | res += C[x]; 13 | } 14 | return res; 15 | } 16 | void change(int x, int c) { 17 | for (; x <= n; x += x & (-x)) { 18 | C[x] += c; 19 | } 20 | } 21 | 22 | int main() { 23 | cin >> n; 24 | for (int i = 1; i <= n; ++i) { 25 | int d; 26 | cin >> d; 27 | change(i, d); 28 | } 29 | for (int i = 1; i <= n; ++i) { 30 | cout << getsum(i) << " "; 31 | } 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /11_expert_datastructure/code/4_2d_binary_index_tree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | const int MAX_N = 810; 4 | int C[MAX_N][MAX_N]; 5 | int n; 6 | int lowbit(int x) { 7 | return x & (-x); 8 | } 9 | void change(int i, int j, int delta){ 10 | for(int x = i; x <= n; x += lowbit(x)) { 11 | for(int y = j; y <= n; y += lowbit(y)) { 12 | C[x][y] += delta; 13 | } 14 | } 15 | } 16 | int getsum(int i, int j){ 17 | int res = 0; 18 | for(int x = i; x; x -= lowbit(x)) { 19 | for(int y = j; y; y -= lowbit(y)) { 20 | res += C[x][y]; 21 | } 22 | } 23 | return res; 24 | } 25 | 26 | int main() { 27 | cin >> n; 28 | for (int i = 1; i <= n; ++i) { 29 | for (int j = 1; j <= n; ++j) { 30 | int d; 31 | cin >> d; 32 | change(i, j, d); 33 | } 34 | } 35 | int x, y; 36 | cin >> x >> y; 37 | cout << getsum(x, y) << endl; 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /11_expert_datastructure/code/5_discretization.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | const int maxn = 10010; 6 | int num[maxn]; 7 | int temp[maxn]; 8 | int n; 9 | map mp; 10 | int main() { 11 | cin >> n; 12 | for (int i = 0; i < n; ++i) { 13 | cin >> num[i]; 14 | temp[i] = num[i]; 15 | } 16 | sort(temp, temp + n); 17 | int m = unique(temp, temp + n) - temp; 18 | for (int i = 0; i < m; ++i) { 19 | mp[temp[i]] = i + 1; 20 | } 21 | for (int i = 0; i < n; ++i) { 22 | cout << mp[num[i]] << " "; 23 | } 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /11_expert_datastructure/content.md: -------------------------------------------------------------------------------- 1 | # 线段树和树状数组模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|线段树单点更新模板|1|| 7 | |跟随|线段树区间更新模板|1|| 8 | |跟随|树状数组模板|1|| 9 | |跟随|二维树状数组模板|1|| 10 | |跟随|离散化模板|1|| 11 | 12 | 线段树和树状数组。 13 | -------------------------------------------------------------------------------- /1_datastructure/1_vector.md: -------------------------------------------------------------------------------- 1 | # vector 的使用 2 | - vector 3 | 4 | ### 文件名 5 | vector.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 首先我们引入需要的头文件`vector`,在代码头部写下 26 | ```c++ 27 | #include 28 | ``` 29 | 30 | #### 代码 31 | ```c++ 32 | #include 33 | #include 34 | using namespace std; 35 | int main() { 36 | 37 | return 0; 38 | } 39 | ``` 40 | 41 | --- 42 | ### 第二步 43 | #### 讲解 44 | 首先我们学习如何使用一维的`vector`。 45 | 46 | 在`main`函数里面通过`vector v`来定义一个储存整数的空的`vector`。当然`vector`可以存任何类型的数据,比如`vector v`等等。在本课中我们用 int 来举例。 47 | ```c++ 48 | vector v; 49 | ``` 50 | 51 | #### 提示 52 | ```c++ 53 | #include 54 | #include 55 | using namespace std; 56 | int main() { 57 | vector v; 58 | 59 | return 0; 60 | } 61 | ``` 62 | 63 | #### 代码 64 | ```c++ 65 | #include 66 | #include 67 | using namespace std; 68 | int main() { 69 | vector v; 70 | 71 | return 0; 72 | } 73 | ``` 74 | 75 | --- 76 | ### 第三步 77 | #### 讲解 78 | 我们把 $1$ 到 $10$ 的平方依次存到`vector`。 79 | 在刚才的定义下面写下 80 | ```c++ 81 | for (int i = 1; i <= 10; ++i) { 82 | v.push_back(i * i); 83 | } 84 | ``` 85 | 86 | #### 提示 87 | ```c++ 88 | #include 89 | #include 90 | using namespace std; 91 | int main() { 92 | vector v; 93 | for (int i = 1; i <= 10; ++i) { 94 | v.push_back(i * i); 95 | } 96 | 97 | return 0; 98 | } 99 | ``` 100 | 101 | 102 | #### 代码 103 | ```c++ 104 | #include 105 | #include 106 | using namespace std; 107 | int main() { 108 | vector v; 109 | for (int i = 1; i <= 10; ++i) { 110 | v.push_back(i * i); 111 | } 112 | 113 | return 0; 114 | } 115 | ``` 116 | 117 | 118 | --- 119 | ### 第四步 120 | #### 讲解 121 | 然后依次输出我们刚才压入`vector`的值。继续写下 122 | ```c++ 123 | for (int i = 0; i < v.size(); ++i) { 124 | cout << v[i] << " "; 125 | } 126 | cout << endl; 127 | ``` 128 | 129 | #### 提示 130 | ```c++ 131 | #include 132 | #include 133 | using namespace std; 134 | int main() { 135 | vector v; 136 | for (int i = 1; i <= 10; ++i) { 137 | v.push_back(i * i); 138 | } 139 | for (int i = 0; i < v.size(); ++i) { 140 | cout << v[i] << " "; 141 | } 142 | cout << endl; 143 | 144 | return 0; 145 | } 146 | ``` 147 | 148 | 149 | #### 代码 150 | ```c++ 151 | #include 152 | #include 153 | using namespace std; 154 | int main() { 155 | vector v; 156 | for (int i = 1; i <= 10; ++i) { 157 | v.push_back(i * i); 158 | } 159 | for (int i = 0; i < v.size(); ++i) { 160 | cout << v[i] << " "; 161 | } 162 | cout << endl; 163 | 164 | return 0; 165 | } 166 | ``` 167 | 168 | --- 169 | ### 第五步 170 | #### 讲解 171 | 到这里,里可以点击**运行**看看效果。 172 | 173 | 接下来我们学习二维的`vector`的使用。二维的`vector`就是一个`vector`里面在套一个`vector`。通过如下的代码定义一个空的二维的`vector`。在刚才的输出代码后面继续写下,**特别注意:里面的`vector`后面有一个空格,这个空格不能到少,因为在没有开启 c++11 的情况下,会被识别成一个`>>`**。 174 | ```c++ 175 | vector > v2d; 176 | ``` 177 | 178 | #### 提示 179 | ```c++ 180 | #include 181 | #include 182 | using namespace std; 183 | int main() { 184 | vector v; 185 | for (int i = 1; i <= 10; ++i) { 186 | v.push_back(i * i); 187 | } 188 | for (int i = 0; i < v.size(); ++i) { 189 | cout << v[i] << " "; 190 | } 191 | cout << endl; 192 | vector > v2d; 193 | for (int i = 0; i < 5; ++i) { 194 | v2d.push_back(vector()); 195 | } 196 | 197 | return 0; 198 | } 199 | ``` 200 | 201 | #### 代码 202 | ```c++ 203 | #include 204 | #include 205 | using namespace std; 206 | int main() { 207 | vector v; 208 | for (int i = 1; i <= 10; ++i) { 209 | v.push_back(i * i); 210 | } 211 | for (int i = 0; i < v.size(); ++i) { 212 | cout << v[i] << " "; 213 | } 214 | cout << endl; 215 | vector > v2d; 216 | 217 | return 0; 218 | } 219 | ``` 220 | 221 | 222 | 223 | 224 | --- 225 | ### 第六步 226 | #### 讲解 227 | 接下来我们给第一维赋值,第一维是一个一维的`vector`,在定义后面写下: 228 | ```c++ 229 | for (int i = 0; i < 5; ++i) { 230 | v2d.push_back(vector()); 231 | } 232 | ``` 233 | 234 | #### 提示 235 | ```c++ 236 | #include 237 | #include 238 | using namespace std; 239 | int main() { 240 | vector v; 241 | for (int i = 1; i <= 10; ++i) { 242 | v.push_back(i * i); 243 | } 244 | for (int i = 0; i < v.size(); ++i) { 245 | cout << v[i] << " "; 246 | } 247 | cout << endl; 248 | vector > v2d; 249 | for (int i = 0; i < 5; ++i) { 250 | v2d.push_back(vector()); 251 | } 252 | 253 | return 0; 254 | } 255 | ``` 256 | 257 | #### 代码 258 | ```c++ 259 | #include 260 | #include 261 | using namespace std; 262 | int main() { 263 | vector v; 264 | for (int i = 1; i <= 10; ++i) { 265 | v.push_back(i * i); 266 | } 267 | for (int i = 0; i < v.size(); ++i) { 268 | cout << v[i] << " "; 269 | } 270 | cout << endl; 271 | vector > v2d; 272 | for (int i = 0; i < 5; ++i) { 273 | v2d.push_back(vector()); 274 | } 275 | 276 | return 0; 277 | } 278 | ``` 279 | 280 | --- 281 | ### 第七步 282 | #### 讲解 283 | 我们让第 $i$ 行第 $j$ 列的元素的值为 $i*j$。继续写下下面的代码: 284 | ```c++ 285 | for (int i = 0; i < v2d.size(); ++i) { 286 | for (int j = 0; j < 5; ++j) { 287 | v2d[i].push_back(i * j); 288 | } 289 | } 290 | ``` 291 | 292 | 293 | 294 | #### 提示 295 | ```c++ 296 | #include 297 | #include 298 | using namespace std; 299 | int main() { 300 | vector v; 301 | for (int i = 1; i <= 10; ++i) { 302 | v.push_back(i * i); 303 | } 304 | for (int i = 0; i < v.size(); ++i) { 305 | cout << v[i] << " "; 306 | } 307 | cout << endl; 308 | vector > v2d; 309 | for (int i = 0; i < 5; ++i) { 310 | v2d.push_back(vector()); 311 | } 312 | for (int i = 0; i < v2d.size(); ++i) { 313 | for (int j = 0; j < 5; ++j) { 314 | v2d[i].push_back(i * j); 315 | } 316 | } 317 | 318 | return 0; 319 | } 320 | ``` 321 | 322 | 323 | #### 代码 324 | ```c++ 325 | #include 326 | #include 327 | using namespace std; 328 | int main() { 329 | vector v; 330 | for (int i = 1; i <= 10; ++i) { 331 | v.push_back(i * i); 332 | } 333 | for (int i = 0; i < v.size(); ++i) { 334 | cout << v[i] << " "; 335 | } 336 | cout << endl; 337 | vector > v2d; 338 | for (int i = 0; i < 5; ++i) { 339 | v2d.push_back(vector()); 340 | } 341 | for (int i = 0; i < v2d.size(); ++i) { 342 | for (int j = 0; j < 5; ++j) { 343 | v2d[i].push_back(i * j); 344 | } 345 | } 346 | 347 | return 0; 348 | } 349 | ``` 350 | 351 | 352 | 353 | --- 354 | ### 第八步 355 | #### 讲解 356 | 然后我们输出一个 $5$ 行 $5$ 列的矩阵。 357 | ```c++ 358 | for (int i = 0; i < v2d.size(); ++i) { 359 | for (int j = 0; j < v2d[i].size(); ++j) { 360 | cout << v2d[i][j] << " "; 361 | } 362 | cout << endl; 363 | } 364 | ``` 365 | 366 | #### 提示 367 | ```c++ 368 | #include 369 | #include 370 | using namespace std; 371 | int main() { 372 | vector v; 373 | for (int i = 1; i <= 10; ++i) { 374 | v.push_back(i * i); 375 | } 376 | for (int i = 0; i < v.size(); ++i) { 377 | cout << v[i] << " "; 378 | } 379 | cout << endl; 380 | vector > v2d; 381 | for (int i = 0; i < 5; ++i) { 382 | v2d.push_back(vector()); 383 | } 384 | for (int i = 0; i < v2d.size(); ++i) { 385 | for (int j = 0; j < 5; ++j) { 386 | v2d[i].push_back(i * j); 387 | } 388 | } 389 | for (int i = 0; i < v2d.size(); ++i) { 390 | for (int j = 0; j < v2d[i].size(); ++j) { 391 | cout << v2d[i][j] << " "; 392 | } 393 | cout << endl; 394 | } 395 | return 0; 396 | } 397 | ``` 398 | 399 | 400 | #### 代码 401 | ```c++ 402 | #include 403 | #include 404 | using namespace std; 405 | int main() { 406 | vector v; 407 | for (int i = 1; i <= 10; ++i) { 408 | v.push_back(i * i); 409 | } 410 | for (int i = 0; i < v.size(); ++i) { 411 | cout << v[i] << " "; 412 | } 413 | cout << endl; 414 | vector > v2d; 415 | for (int i = 0; i < 5; ++i) { 416 | v2d.push_back(vector()); 417 | } 418 | for (int i = 0; i < v2d.size(); ++i) { 419 | for (int j = 0; j < 5; ++j) { 420 | v2d[i].push_back(i * j); 421 | } 422 | } 423 | for (int i = 0; i < v2d.size(); ++i) { 424 | for (int j = 0; j < v2d[i].size(); ++j) { 425 | cout << v2d[i][j] << " "; 426 | } 427 | cout << endl; 428 | } 429 | return 0; 430 | } 431 | ``` 432 | 433 | 434 | 435 | 436 | --- 437 | ### 完成讲解 438 | 这一节已经完成了,赶紧运行看看效果。 439 | 440 | 聪明的你一定学会了`vector`怎么用了。 441 | -------------------------------------------------------------------------------- /1_datastructure/2_set.md: -------------------------------------------------------------------------------- 1 | # set 的使用 2 | - set 3 | 4 | ### 文件名 5 | set.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 首先我们引入需要的头文件`set`,在代码头部写下 26 | ```c++ 27 | #include 28 | ``` 29 | 30 | #### 代码 31 | ```c++ 32 | #include 33 | #include 34 | using namespace std; 35 | int main() { 36 | 37 | return 0; 38 | } 39 | ``` 40 | 41 | --- 42 | ### 第二步 43 | #### 讲解 44 | 首先我们学习如何创建一个`set`。 45 | 46 | 在`main`函数里面通过`set country`来定义一个储存字符串的空的`set`。当然`set`可以存任何类型的数据,比如`set s`等等。在本课中我们用 string 来举例。 47 | ```c++ 48 | set country; 49 | ``` 50 | 51 | #### 提示 52 | ```c++ 53 | #include 54 | #include 55 | using namespace std; 56 | int main() { 57 | set country; 58 | 59 | return 0; 60 | } 61 | ``` 62 | 63 | #### 代码 64 | ```c++ 65 | #include 66 | #include 67 | using namespace std; 68 | int main() { 69 | set country; 70 | 71 | return 0; 72 | } 73 | ``` 74 | 75 | --- 76 | ### 第三步 77 | #### 讲解 78 | 我们把`China` `America` `France`依次插入到`set`。 79 | 在刚才的定义下面写下 80 | ```c++ 81 | country.insert("China"); 82 | country.insert("America"); 83 | country.insert("France"); 84 | ``` 85 | 86 | #### 提示 87 | ```c++ 88 | #include 89 | #include 90 | using namespace std; 91 | int main() { 92 | set country; 93 | country.insert("China"); 94 | country.insert("America"); 95 | country.insert("France"); 96 | 97 | return 0; 98 | } 99 | ``` 100 | 101 | 102 | #### 代码 103 | ```c++ 104 | #include 105 | #include 106 | using namespace std; 107 | int main() { 108 | set country; 109 | country.insert("China"); 110 | country.insert("America"); 111 | country.insert("France"); 112 | 113 | return 0; 114 | } 115 | ``` 116 | 117 | 118 | --- 119 | ### 第四步 120 | #### 讲解 121 | 然后依次输出我们刚才插入`set`的字符串。继续写下 122 | ```c++ 123 | set::iterator it; 124 | for (it = country.begin(); it != country.end(); ++it) { 125 | cout << * it << " "; 126 | } 127 | cout << endl; 128 | ``` 129 | 130 | #### 提示 131 | ```c++ 132 | #include 133 | #include 134 | using namespace std; 135 | int main() { 136 | set country; 137 | country.insert("China"); 138 | country.insert("America"); 139 | country.insert("France"); 140 | set::iterator it; 141 | for (it = country.begin(); it != country.end(); ++it) { 142 | cout << * it << " "; 143 | } 144 | cout << endl; 145 | 146 | return 0; 147 | } 148 | ``` 149 | 150 | 151 | #### 代码 152 | ```c++ 153 | #include 154 | #include 155 | using namespace std; 156 | int main() { 157 | set country; 158 | country.insert("China"); 159 | country.insert("America"); 160 | country.insert("France"); 161 | set::iterator it; 162 | for (it = country.begin(); it != country.end(); ++it) { 163 | cout << * it << " "; 164 | } 165 | cout << endl; 166 | 167 | return 0; 168 | } 169 | ``` 170 | 171 | --- 172 | ### 第五步 173 | #### 讲解 174 | 到这里,里可以点击**运行**看看效果。 175 | 可以发现我们刚才插入的字符串按照字典序排列好了。 176 | 177 | 接下来我们学习`set`的删除操作。 178 | ```c++ 179 | country.erase("American"); 180 | country.erase("England"); 181 | ``` 182 | 183 | #### 提示 184 | ```c++ 185 | #include 186 | #include 187 | using namespace std; 188 | int main() { 189 | set country; 190 | country.insert("China"); 191 | country.insert("America"); 192 | country.insert("France"); 193 | set::iterator it; 194 | for (it = country.begin(); it != country.end(); ++it) { 195 | cout << * it << " "; 196 | } 197 | cout << endl; 198 | country.erase("American"); 199 | country.erase("England"); 200 | 201 | return 0; 202 | } 203 | ``` 204 | 205 | #### 代码 206 | ```c++ 207 | #include 208 | #include 209 | using namespace std; 210 | int main() { 211 | set country; 212 | country.insert("China"); 213 | country.insert("America"); 214 | country.insert("France"); 215 | set::iterator it; 216 | for (it = country.begin(); it != country.end(); ++it) { 217 | cout << * it << " "; 218 | } 219 | cout << endl; 220 | country.erase("American"); 221 | country.erase("England"); 222 | 223 | return 0; 224 | } 225 | ``` 226 | 227 | 228 | 229 | 230 | --- 231 | ### 第六步 232 | #### 讲解 233 | 接下来我们统计`set`中`China`字符串的个数 234 | ```c++ 235 | if (country.count("China")) { 236 | cout << "China in country." << endl; 237 | } 238 | ``` 239 | 240 | #### 提示 241 | ```c++ 242 | #include 243 | #include 244 | using namespace std; 245 | int main() { 246 | set country; 247 | country.insert("China"); 248 | country.insert("America"); 249 | country.insert("France"); 250 | set::iterator it; 251 | for (it = country.begin(); it != country.end(); ++it) { 252 | cout << * it << " "; 253 | } 254 | cout << endl; 255 | country.erase("American"); 256 | country.erase("England"); 257 | if (country.count("China")) { 258 | cout << "China in country." << endl; 259 | } 260 | 261 | return 0; 262 | } 263 | ``` 264 | 265 | #### 代码 266 | ```c++ 267 | #include 268 | #include 269 | using namespace std; 270 | int main() { 271 | set country; 272 | country.insert("China"); 273 | country.insert("America"); 274 | country.insert("France"); 275 | set::iterator it; 276 | for (it = country.begin(); it != country.end(); ++it) { 277 | cout << * it << " "; 278 | } 279 | cout << endl; 280 | country.erase("American"); 281 | country.erase("England"); 282 | if (country.count("China")) { 283 | cout << "China in country." << endl; 284 | } 285 | 286 | return 0; 287 | } 288 | ``` 289 | 290 | --- 291 | ### 第七步 292 | #### 讲解 293 | 最后我们将使用完成的`set`清空。继续写下下面的代码: 294 | ```c++ 295 | country.clear(); 296 | ``` 297 | 298 | 299 | 300 | #### 提示 301 | ```c++ 302 | #include 303 | #include 304 | using namespace std; 305 | int main() { 306 | set country; 307 | country.insert("China"); 308 | country.insert("America"); 309 | country.insert("France"); 310 | set::iterator it; 311 | for (it = country.begin(); it != country.end(); ++it) { 312 | cout << * it << " "; 313 | } 314 | cout << endl; 315 | country.erase("American"); 316 | country.erase("England"); 317 | if (country.count("China")) { 318 | cout << "China in country." << endl; 319 | } 320 | country.clear(); 321 | 322 | return 0; 323 | } 324 | ``` 325 | 326 | 327 | #### 代码 328 | ```c++ 329 | #include 330 | #include 331 | using namespace std; 332 | int main() { 333 | set country; 334 | country.insert("China"); 335 | country.insert("America"); 336 | country.insert("France"); 337 | set::iterator it; 338 | for (it = country.begin(); it != country.end(); ++it) { 339 | cout << * it << " "; 340 | } 341 | cout << endl; 342 | country.erase("American"); 343 | country.erase("England"); 344 | if (country.count("China")) { 345 | cout << "China in country." << endl; 346 | } 347 | country.clear(); 348 | 349 | return 0; 350 | } 351 | ``` 352 | 353 | 354 | 355 | --- 356 | ### 完成讲解 357 | 这一节已经完成了,赶紧运行看看效果。 358 | 359 | 聪明的你一定学会了`set`怎么用了。 360 | -------------------------------------------------------------------------------- /1_datastructure/3_map.md: -------------------------------------------------------------------------------- 1 | # map 的使用 2 | - map 3 | 4 | ### 文件名 5 | map.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 首先我们引入需要的头文件`map`,在代码头部写下 26 | ```c++ 27 | #include 28 | #include 29 | ``` 30 | 31 | #### 代码 32 | ```c++ 33 | #include 34 | #include 35 | #include 36 | using namespace std; 37 | int main() { 38 | 39 | return 0; 40 | } 41 | ``` 42 | 43 | --- 44 | ### 第二步 45 | #### 讲解 46 | 首先我们学习如何创建一个`map`。 47 | 48 | 在`main`函数里面通过`map dict`来定义一个`key:value`映射关系的空的`map`。当然`map`可以存任何类型的数据,比如`map m`等等。在本课中我们用`string:int`来举例。 49 | ```c++ 50 | map dict; 51 | ``` 52 | 53 | #### 提示 54 | ```c++ 55 | #include 56 | #include 57 | #include 58 | using namespace std; 59 | int main() { 60 | map dict; 61 | 62 | return 0; 63 | } 64 | ``` 65 | 66 | #### 代码 67 | ```c++ 68 | #include 69 | #include 70 | #include 71 | using namespace std; 72 | int main() { 73 | map dict; 74 | 75 | return 0; 76 | } 77 | ``` 78 | 79 | --- 80 | ### 第三步 81 | #### 讲解 82 | 我们把`Tom` `Jone` `Mary`依次插入到`map`并一一对应的赋值。 83 | 在刚才的定义下面写下 84 | ```c++ 85 | dict["Tom"] = 1; 86 | dict["Jone"] = 2; 87 | dict["Mary"] = 1; 88 | ``` 89 | 90 | #### 提示 91 | ```c++ 92 | #include 93 | #include 94 | #include 95 | using namespace std; 96 | int main() { 97 | map dict; 98 | dict["Tom"] = 1; 99 | dict["Jone"] = 2; 100 | dict["Mary"] = 1; 101 | 102 | return 0; 103 | } 104 | ``` 105 | 106 | 107 | #### 代码 108 | ```c++ 109 | #include 110 | #include 111 | #include 112 | using namespace std; 113 | int main() { 114 | map dict; 115 | dict["Tom"] = 1; 116 | dict["Jone"] = 2; 117 | dict["Mary"] = 1; 118 | 119 | return 0; 120 | } 121 | ``` 122 | 123 | 124 | --- 125 | ### 第四步 126 | #### 讲解 127 | 接下来我们查看`map`中`Mary`对应的value值,先判断一下`map`中是否有`Mary`。继续写下 128 | ```c++ 129 | if (dict.count("Mary")) { 130 | cout << "Mary is in class " << dict["Mary"]; 131 | } 132 | ``` 133 | 134 | #### 提示 135 | ```c++ 136 | #include 137 | #include 138 | #include 139 | using namespace std; 140 | int main() { 141 | map dict; 142 | dict["Tom"] = 1; 143 | dict["Jone"] = 2; 144 | dict["Mary"] = 1; 145 | if (dict.count("Mary")) { 146 | cout << "Mary is in class " << dict["Mary"]; 147 | } 148 | 149 | return 0; 150 | } 151 | ``` 152 | 153 | 154 | #### 代码 155 | ```c++ 156 | #include 157 | #include 158 | #include 159 | using namespace std; 160 | int main() { 161 | map dict; 162 | dict["Tom"] = 1; 163 | dict["Jone"] = 2; 164 | dict["Mary"] = 1; 165 | if (dict.count("Mary")) { 166 | cout << "Mary is in class " << dict["Mary"]; 167 | } 168 | 169 | return 0; 170 | } 171 | ``` 172 | 173 | --- 174 | ### 第五步 175 | #### 讲解 176 | 到这里,里可以点击**运行**看看效果。 177 | 178 | 接下来我们学习`map`的遍历操作。 179 | ```c++ 180 | for (map::iterator it = dict.begin(); it != dict.end(); ++it) { 181 | cout << it->first << " is in class " << it->second << endl; 182 | } 183 | ``` 184 | 185 | #### 提示 186 | ```c++ 187 | #include 188 | #include 189 | #include 190 | using namespace std; 191 | int main() { 192 | map dict; 193 | dict["Tom"] = 1; 194 | dict["Jone"] = 2; 195 | dict["Mary"] = 1; 196 | if (dict.count("Mary")) { 197 | cout << "Mary is in class " << dict["Mary"]; 198 | } 199 | for (map::iterator it = dict.begin(); it != dict.end(); ++it) { 200 | cout << it->first << " is in class " << it->second << endl; 201 | } 202 | 203 | return 0; 204 | } 205 | ``` 206 | 207 | #### 代码 208 | ```c++ 209 | #include 210 | #include 211 | #include 212 | using namespace std; 213 | int main() { 214 | map dict; 215 | dict["Tom"] = 1; 216 | dict["Jone"] = 2; 217 | dict["Mary"] = 1; 218 | if (dict.count("Mary")) { 219 | cout << "Mary is in class " << dict["Mary"]; 220 | } 221 | for (map::iterator it = dict.begin(); it != dict.end(); ++it) { 222 | cout << it->first << " is in class " << it->second << endl; 223 | } 224 | 225 | return 0; 226 | } 227 | ``` 228 | 229 | 230 | 231 | 232 | --- 233 | ### 第六步 234 | #### 讲解 235 | 到这里,里可以点击**运行**看看效果。 236 | 可以发现`map`里的key字符串按照字典序排列好了。 237 | 238 | 最后我们再清空`map` 239 | ```c++ 240 | dict.clear(); 241 | ``` 242 | 243 | #### 提示 244 | ```c++ 245 | #include 246 | #include 247 | #include 248 | using namespace std; 249 | int main() { 250 | map dict; 251 | dict["Tom"] = 1; 252 | dict["Jone"] = 2; 253 | dict["Mary"] = 1; 254 | if (dict.count("Mary")) { 255 | cout << "Mary is in class " << dict["Mary"]; 256 | } 257 | for (map::iterator it = dict.begin(); it != dict.end(); ++it) { 258 | cout << it->first << " is in class " << it->second << endl; 259 | } 260 | dict.clear(); 261 | return 0; 262 | } 263 | ``` 264 | 265 | #### 代码 266 | ```c++ 267 | #include 268 | #include 269 | #include 270 | using namespace std; 271 | int main() { 272 | map dict; 273 | dict["Tom"] = 1; 274 | dict["Jone"] = 2; 275 | dict["Mary"] = 1; 276 | if (dict.count("Mary")) { 277 | cout << "Mary is in class " << dict["Mary"]; 278 | } 279 | for (map::iterator it = dict.begin(); it != dict.end(); ++it) { 280 | cout << it->first << " is in class " << it->second << endl; 281 | } 282 | dict.clear(); 283 | return 0; 284 | } 285 | ``` 286 | 287 | 288 | --- 289 | ### 完成讲解 290 | 这一节已经完成了,赶紧运行看看效果。 291 | 292 | 聪明的你一定学会了`map`怎么用了。 293 | -------------------------------------------------------------------------------- /1_datastructure/4_stack.md: -------------------------------------------------------------------------------- 1 | # stack 的使用 2 | - stack 3 | 4 | ### 文件名 5 | stack.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 首先我们引入需要的头文件`stack`,在代码头部写下 26 | ```c++ 27 | #include 28 | ``` 29 | 30 | #### 代码 31 | ```c++ 32 | #include 33 | #include 34 | using namespace std; 35 | int main() { 36 | 37 | return 0; 38 | } 39 | ``` 40 | 41 | --- 42 | ### 第二步 43 | #### 讲解 44 | 首先我们学习如何使用`stack`。 45 | 46 | 在`main`函数上方通过`stack S`来定义一个全局栈来储存整数的空的`stack`。当然`stack`可以存任何类型的数据,比如`stack S`等等。在本课中我们用 int 来举例。 47 | ```c++ 48 | stack S; 49 | ``` 50 | 51 | #### 提示 52 | ```c++ 53 | #include 54 | #include 55 | using namespace std; 56 | stack S; 57 | int main() { 58 | 59 | return 0; 60 | } 61 | ``` 62 | 63 | #### 代码 64 | ```c++ 65 | #include 66 | #include 67 | using namespace std; 68 | stack S; 69 | int main() { 70 | 71 | return 0; 72 | } 73 | ``` 74 | 75 | --- 76 | ### 第三步 77 | #### 讲解 78 | 我们把`1` `10` `7`三个数依次存到`stack`。 79 | 在`main`函数里写下 80 | ```c++ 81 | S.push(1); 82 | S.push(10); 83 | S.push(7); 84 | ``` 85 | 86 | #### 提示 87 | ```c++ 88 | #include 89 | #include 90 | using namespace std; 91 | stack S; 92 | int main() { 93 | S.push(1); 94 | S.push(10); 95 | S.push(7); 96 | 97 | return 0; 98 | } 99 | ``` 100 | 101 | 102 | #### 代码 103 | ```c++ 104 | #include 105 | #include 106 | using namespace std; 107 | stack S; 108 | int main() { 109 | S.push(1); 110 | S.push(10); 111 | S.push(7); 112 | 113 | return 0; 114 | } 115 | ``` 116 | 117 | 118 | --- 119 | ### 第四步 120 | #### 讲解 121 | 然后我们判断一下`stack`的是否为空。继续写下 122 | ```c++ 123 | while (!S.empty()) { 124 | 125 | } 126 | ``` 127 | 128 | #### 提示 129 | ```c++ 130 | #include 131 | #include 132 | using namespace std; 133 | stack S; 134 | int main() { 135 | S.push(1); 136 | S.push(10); 137 | S.push(7); 138 | while (!S.empty()) { 139 | 140 | } 141 | 142 | return 0; 143 | } 144 | ``` 145 | 146 | 147 | #### 代码 148 | ```c++ 149 | #include 150 | #include 151 | using namespace std; 152 | stack S; 153 | int main() { 154 | S.push(1); 155 | S.push(10); 156 | S.push(7); 157 | while (!S.empty()) { 158 | 159 | } 160 | 161 | return 0; 162 | } 163 | ``` 164 | 165 | --- 166 | ### 第五步 167 | #### 讲解 168 | 如果`stack`不为空,我们将`stack`里的数依次取出来,在`while`里继续写下 169 | ```c++ 170 | cout << S.top() << endl; 171 | S.pop(); 172 | ``` 173 | 174 | #### 提示 175 | ```c++ 176 | #include 177 | #include 178 | using namespace std; 179 | stack S; 180 | int main() { 181 | S.push(1); 182 | S.push(10); 183 | S.push(7); 184 | while (!S.empty()) { 185 | cout << S.top() << endl; 186 | S.pop(); 187 | } 188 | return 0; 189 | } 190 | ``` 191 | 192 | #### 代码 193 | ```c++ 194 | #include 195 | #include 196 | using namespace std; 197 | stack S; 198 | int main() { 199 | S.push(1); 200 | S.push(10); 201 | S.push(7); 202 | while (!S.empty()) { 203 | cout << S.top() << endl; 204 | S.pop(); 205 | } 206 | return 0; 207 | } 208 | ``` 209 | 210 | 211 | 212 | 213 | --- 214 | ### 完成讲解 215 | 这一节已经完成了,赶紧运行看看效果,可以发现`stack`有先进后出的特性。 216 | 217 | 聪明的你一定学会了`stack`怎么用了。 218 | -------------------------------------------------------------------------------- /1_datastructure/5_queue.md: -------------------------------------------------------------------------------- 1 | # queue 的使用 2 | - queue 3 | 4 | ### 文件名 5 | queue.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 首先我们引入需要的头文件`queue`,在代码头部写下 26 | ```c++ 27 | #include 28 | ``` 29 | 30 | #### 代码 31 | ```c++ 32 | #include 33 | #include 34 | using namespace std; 35 | int main() { 36 | 37 | return 0; 38 | } 39 | ``` 40 | 41 | --- 42 | ### 第二步 43 | #### 讲解 44 | 首先我们学习如何使用`queue`。 45 | 46 | 在`main`函数里面通过`queue q`来定义一个储存整数的空的`queue`。当然`queue`可以存任何类型的数据,比如`queue q`等等。在本课中我们用 int 来举例。 47 | ```c++ 48 | queue q; 49 | ``` 50 | 51 | #### 提示 52 | ```c++ 53 | #include 54 | #include 55 | using namespace std; 56 | int main() { 57 | queue q; 58 | 59 | return 0; 60 | } 61 | ``` 62 | 63 | #### 代码 64 | ```c++ 65 | #include 66 | #include 67 | using namespace std; 68 | int main() { 69 | queue q; 70 | 71 | return 0; 72 | } 73 | ``` 74 | 75 | --- 76 | ### 第三步 77 | #### 讲解 78 | 我们把`1` `2` `3`三个数依次存到`queue`。 79 | 在`main`函数里写下 80 | ```c++ 81 | q.push(1); 82 | q.push(2); 83 | q.push(3); 84 | ``` 85 | 86 | #### 提示 87 | ```c++ 88 | #include 89 | #include 90 | using namespace std; 91 | int main() { 92 | queue q; 93 | q.push(1); 94 | q.push(2); 95 | q.push(3); 96 | 97 | return 0; 98 | } 99 | ``` 100 | 101 | 102 | #### 代码 103 | ```c++ 104 | #include 105 | #include 106 | using namespace std; 107 | int main() { 108 | queue q; 109 | q.push(1); 110 | q.push(2); 111 | q.push(3); 112 | 113 | return 0; 114 | } 115 | ``` 116 | 117 | 118 | --- 119 | ### 第四步 120 | #### 讲解 121 | 然后我们判断一下`queue`的是否为空。继续写下 122 | ```c++ 123 | while (!q.empty()) { 124 | 125 | } 126 | ``` 127 | 128 | #### 提示 129 | ```c++ 130 | #include 131 | #include 132 | using namespace std; 133 | int main() { 134 | queue q; 135 | q.push(1); 136 | q.push(2); 137 | q.push(3); 138 | while (!q.empty()) { 139 | 140 | } 141 | 142 | return 0; 143 | } 144 | ``` 145 | 146 | 147 | #### 代码 148 | ```c++ 149 | #include 150 | #include 151 | using namespace std; 152 | int main() { 153 | queue q; 154 | q.push(1); 155 | q.push(2); 156 | q.push(3); 157 | while (!q.empty()) { 158 | 159 | } 160 | 161 | return 0; 162 | } 163 | ``` 164 | 165 | --- 166 | ### 第五步 167 | #### 讲解 168 | 如果`queue`不为空,我们将`queue`里的数依次取出来,在`while`里继续写下 169 | ```c++ 170 | cout << q.front() << endl; 171 | q.pop(); 172 | ``` 173 | 174 | #### 提示 175 | ```c++ 176 | #include 177 | #include 178 | using namespace std; 179 | int main() { 180 | queue q; 181 | q.push(1); 182 | q.push(2); 183 | q.push(3); 184 | while (!q.empty()) { 185 | cout << q.front() << endl; 186 | q.pop(); 187 | } 188 | return 0; 189 | } 190 | ``` 191 | 192 | #### 代码 193 | ```c++ 194 | #include 195 | #include 196 | using namespace std; 197 | int main() { 198 | queue q; 199 | q.push(1); 200 | q.push(2); 201 | q.push(3); 202 | while (!q.empty()) { 203 | cout << q.front() << endl; 204 | q.pop(); 205 | } 206 | return 0; 207 | } 208 | ``` 209 | 210 | 211 | 212 | --- 213 | ### 完成讲解 214 | 这一节已经完成了,赶紧运行看看效果,可以发现`queue`有先进先出的特性。 215 | 216 | 聪明的你一定学会了`queue`怎么用了。 217 | -------------------------------------------------------------------------------- /1_datastructure/6_priority_queue.md: -------------------------------------------------------------------------------- 1 | # priority_queue 的使用 2 | - priority_queue 3 | 4 | ### 文件名 5 | priority_queue.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 首先我们引入需要的头文件`queue`,在代码头部写下 26 | ```c++ 27 | #include 28 | ``` 29 | 30 | #### 代码 31 | ```c++ 32 | #include 33 | #include 34 | using namespace std; 35 | int main() { 36 | 37 | return 0; 38 | } 39 | ``` 40 | 41 | --- 42 | ### 第二步 43 | #### 讲解 44 | 首先我们学习如何使用`priority_queue`。 45 | 46 | 在`main`函数里面通过`priority_queue q`来定义一个储存整数的空的`priority_queue`。当然`priority_queue`可以存任何类型的数据,比如`priority_queue q`等等。在本课中我们用 int 来举例。 47 | ```c++ 48 | priority_queue q; 49 | ``` 50 | 51 | #### 提示 52 | ```c++ 53 | #include 54 | #include 55 | using namespace std; 56 | int main() { 57 | priority_queue q; 58 | 59 | return 0; 60 | } 61 | ``` 62 | 63 | #### 代码 64 | ```c++ 65 | #include 66 | #include 67 | using namespace std; 68 | int main() { 69 | priority_queue q; 70 | 71 | return 0; 72 | } 73 | ``` 74 | 75 | --- 76 | ### 第三步 77 | #### 讲解 78 | 我们把`1` `2` `3`三个数依次存到`priority_queue`。 79 | 在`main`函数里写下 80 | ```c++ 81 | q.push(1); 82 | q.push(2); 83 | q.push(3); 84 | ``` 85 | 86 | #### 提示 87 | ```c++ 88 | #include 89 | #include 90 | using namespace std; 91 | int main() { 92 | priority_queue q; 93 | q.push(1); 94 | q.push(2); 95 | q.push(3); 96 | 97 | return 0; 98 | } 99 | ``` 100 | 101 | 102 | #### 代码 103 | ```c++ 104 | #include 105 | #include 106 | using namespace std; 107 | int main() { 108 | priority_queue q; 109 | q.push(1); 110 | q.push(2); 111 | q.push(3); 112 | 113 | return 0; 114 | } 115 | ``` 116 | 117 | 118 | --- 119 | ### 第四步 120 | #### 讲解 121 | 然后我们判断一下`priority_queue`的是否为空。继续写下 122 | ```c++ 123 | while (!q.empty()) { 124 | 125 | } 126 | ``` 127 | 128 | #### 提示 129 | ```c++ 130 | #include 131 | #include 132 | using namespace std; 133 | int main() { 134 | priority_queue q; 135 | q.push(1); 136 | q.push(2); 137 | q.push(3); 138 | while (!q.empty()) { 139 | 140 | } 141 | 142 | return 0; 143 | } 144 | ``` 145 | 146 | 147 | #### 代码 148 | ```c++ 149 | #include 150 | #include 151 | using namespace std; 152 | int main() { 153 | priority_queue q; 154 | q.push(1); 155 | q.push(2); 156 | q.push(3); 157 | while (!q.empty()) { 158 | 159 | } 160 | 161 | return 0; 162 | } 163 | ``` 164 | 165 | --- 166 | ### 第五步 167 | #### 讲解 168 | 如果`priority_queue`不为空,我们将`priority_queue`里的数依次取出来,在`while`里继续写下 169 | ```c++ 170 | cout << q.top() << endl; 171 | q.pop(); 172 | ``` 173 | 174 | #### 提示 175 | ```c++ 176 | #include 177 | #include 178 | using namespace std; 179 | int main() { 180 | priority_queue q; 181 | q.push(1); 182 | q.push(2); 183 | q.push(3); 184 | while (!q.empty()) { 185 | cout << q.top() << endl; 186 | q.pop(); 187 | } 188 | return 0; 189 | } 190 | ``` 191 | 192 | #### 代码 193 | ```c++ 194 | #include 195 | #include 196 | using namespace std; 197 | int main() { 198 | priority_queue q; 199 | q.push(1); 200 | q.push(2); 201 | q.push(3); 202 | while (!q.empty()) { 203 | cout << q.top() << endl; 204 | q.pop(); 205 | } 206 | return 0; 207 | } 208 | ``` 209 | 210 | 211 | --- 212 | ### 完成讲解 213 | 这一节已经完成了,赶紧运行看看效果,可以发现`priority_queue`会自动从大到小排好序。 214 | 215 | 聪明的你一定学会了`priority_queue`怎么用了。 216 | -------------------------------------------------------------------------------- /1_datastructure/7_disjoint.md: -------------------------------------------------------------------------------- 1 | # disjoint 的使用 2 | - disjoint 3 | 4 | ### 文件名 5 | disjoint.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 首先我们需要定义节点个数`n`,`father`父节点数组,`dist`距离数组,`size`权值数组,在`main`函数上方写下 26 | ```c++ 27 | int father[110], n; 28 | int dist[110], size[110]; 29 | ``` 30 | 31 | #### 代码 32 | ```c++ 33 | #include 34 | using namespace std; 35 | int father[110], n; 36 | int dist[110], size[110]; 37 | int main() { 38 | 39 | return 0; 40 | } 41 | ``` 42 | 43 | --- 44 | ### 第二步 45 | #### 讲解 46 | 然后对这些数组进行初始化。在`main`函数里写下 47 | ```c++ 48 | n = 10; 49 | init(); 50 | ``` 51 | 52 | #### 提示 53 | ```c++ 54 | #include 55 | using namespace std; 56 | int father[110], n; 57 | int dist[110], size[110]; 58 | int main() { 59 | n = 10; 60 | init(); 61 | 62 | return 0; 63 | } 64 | ``` 65 | 66 | #### 代码 67 | ```c++ 68 | #include 69 | using namespace std; 70 | int father[110], n; 71 | int dist[110], size[110]; 72 | int main() { 73 | n = 10; 74 | init(); 75 | 76 | return 0; 77 | } 78 | ``` 79 | 80 | --- 81 | ### 第三步 82 | #### 讲解 83 | 接下来具体实现`init`函数。继续在`main`函数上面写下 84 | ```c++ 85 | void init() { 86 | for (int i = 1; i <= n; ++i) { 87 | father[i] = i, dist[i] = 0, size[i] = 1; 88 | } 89 | } 90 | ``` 91 | 92 | #### 提示 93 | ```c++ 94 | #include 95 | using namespace std; 96 | int father[110], n; 97 | int dist[110], size[110]; 98 | 99 | void init() { 100 | for (int i = 1; i <= n; ++i) { 101 | father[i] = i, dist[i] = 0, size[i] = 1; 102 | } 103 | } 104 | int main() { 105 | n = 10; 106 | init(); 107 | 108 | return 0; 109 | } 110 | ``` 111 | 112 | 113 | #### 代码 114 | ```c++ 115 | #include 116 | using namespace std; 117 | int father[110], n; 118 | int dist[110], size[110]; 119 | 120 | void init() { 121 | for (int i = 1; i <= n; ++i) { 122 | father[i] = i, dist[i] = 0, size[i] = 1; 123 | } 124 | } 125 | int main() { 126 | n = 10; 127 | init(); 128 | 129 | return 0; 130 | } 131 | ``` 132 | 133 | 134 | --- 135 | ### 第四步 136 | #### 讲解 137 | 现在开始学习并查集的`merge`操作,我们将`1`和`2`、`10`和`7`、`3`和`4`、`3`和`7`合并。在`main`函数里继续写下 138 | ```c++ 139 | merge(1, 2); 140 | merge(10, 7); 141 | merge(3, 4); 142 | merge(3, 7); 143 | ``` 144 | 145 | #### 提示 146 | ```c++ 147 | #include 148 | using namespace std; 149 | int father[110], n; 150 | int dist[110], size[110]; 151 | 152 | void init() { 153 | for (int i = 1; i <= n; ++i) { 154 | father[i] = i, dist[i] = 0, size[i] = 1; 155 | } 156 | } 157 | int main() { 158 | n = 10; 159 | init(); 160 | merge(1, 2); 161 | merge(10, 7); 162 | merge(3, 4); 163 | merge(3, 7); 164 | 165 | return 0; 166 | } 167 | ``` 168 | 169 | 170 | #### 代码 171 | ```c++ 172 | #include 173 | using namespace std; 174 | int father[110], n; 175 | int dist[110], size[110]; 176 | 177 | void init() { 178 | for (int i = 1; i <= n; ++i) { 179 | father[i] = i, dist[i] = 0, size[i] = 1; 180 | } 181 | } 182 | int main() { 183 | n = 10; 184 | init(); 185 | merge(1, 2); 186 | merge(10, 7); 187 | merge(3, 4); 188 | merge(3, 7); 189 | 190 | return 0; 191 | } 192 | ``` 193 | 194 | --- 195 | ### 第五步 196 | #### 讲解 197 | 接下来我们具体实现`merge`函数。在`main`上方继续写下 198 | ```c++ 199 | void merge(int a, int b) { 200 | a = get(a); 201 | b = get(b); 202 | if (a != b) { 203 | father[a] = b; 204 | dist[a] = size[b]; 205 | size[b] += size[a]; 206 | } 207 | } 208 | ``` 209 | 210 | #### 提示 211 | ```c++ 212 | #include 213 | using namespace std; 214 | int father[110], n; 215 | int dist[110], size[110]; 216 | 217 | void init() { 218 | for (int i = 1; i <= n; ++i) { 219 | father[i] = i, dist[i] = 0, size[i] = 1; 220 | } 221 | } 222 | void merge(int a, int b) { 223 | a = get(a); 224 | b = get(b); 225 | if (a != b) { 226 | father[a] = b; 227 | dist[a] = size[b]; 228 | size[b] += size[a]; 229 | } 230 | } 231 | int main() { 232 | n = 10; 233 | init(); 234 | merge(1, 2); 235 | merge(10, 7); 236 | merge(3, 4); 237 | merge(3, 7); 238 | 239 | return 0; 240 | } 241 | ``` 242 | 243 | #### 代码 244 | ```c++ 245 | #include 246 | using namespace std; 247 | int father[110], n; 248 | int dist[110], size[110]; 249 | 250 | void init() { 251 | for (int i = 1; i <= n; ++i) { 252 | father[i] = i, dist[i] = 0, size[i] = 1; 253 | } 254 | } 255 | void merge(int a, int b) { 256 | a = get(a); 257 | b = get(b); 258 | if (a != b) { 259 | father[a] = b; 260 | dist[a] = size[b]; 261 | size[b] += size[a]; 262 | } 263 | } 264 | int main() { 265 | n = 10; 266 | init(); 267 | merge(1, 2); 268 | merge(10, 7); 269 | merge(3, 4); 270 | merge(3, 7); 271 | 272 | return 0; 273 | } 274 | ``` 275 | 276 | 277 | 278 | 279 | --- 280 | ### 第六步 281 | #### 讲解 282 | 接下来我们查找`1`所在集合的根节点并输出`1`到根节点的距离`dist`。在`main`函数里继续写下 283 | ```c++ 284 | get(1); // 一定要先 get,可能没有直接连接根节点 285 | cout << dist[1] + 1 << endl; 286 | ``` 287 | 288 | #### 提示 289 | ```c++ 290 | #include 291 | using namespace std; 292 | int father[110], n; 293 | int dist[110], size[110]; 294 | 295 | void init() { 296 | for (int i = 1; i <= n; ++i) { 297 | father[i] = i, dist[i] = 0, size[i] = 1; 298 | } 299 | } 300 | void merge(int a, int b) { 301 | a = get(a); 302 | b = get(b); 303 | if (a != b) { 304 | father[a] = b; 305 | dist[a] = size[b]; 306 | size[b] += size[a]; 307 | } 308 | } 309 | int main() { 310 | n = 10; 311 | init(); 312 | merge(1, 2); 313 | merge(10, 7); 314 | merge(3, 4); 315 | merge(3, 7); 316 | get(1); // 一定要先 get,可能没有直接连接根节点 317 | cout << dist[1] + 1 << endl; 318 | 319 | return 0; 320 | } 321 | ``` 322 | 323 | #### 代码 324 | ```c++ 325 | #include 326 | using namespace std; 327 | int father[110], n; 328 | int dist[110], size[110]; 329 | 330 | void init() { 331 | for (int i = 1; i <= n; ++i) { 332 | father[i] = i, dist[i] = 0, size[i] = 1; 333 | } 334 | } 335 | void merge(int a, int b) { 336 | a = get(a); 337 | b = get(b); 338 | if (a != b) { 339 | father[a] = b; 340 | dist[a] = size[b]; 341 | size[b] += size[a]; 342 | } 343 | } 344 | int main() { 345 | n = 10; 346 | init(); 347 | merge(1, 2); 348 | merge(10, 7); 349 | merge(3, 4); 350 | merge(3, 7); 351 | get(1); // 一定要先 get,可能没有直接连接根节点 352 | cout << dist[1] + 1 << endl; 353 | 354 | return 0; 355 | } 356 | ``` 357 | 358 | --- 359 | ### 第七步 360 | #### 讲解 361 | 我们接着实现`get`函数。在`merge`函数上方继续写下 362 | ```c++ 363 | int get(int x) { 364 | if (father[x] == x) { 365 | return x; 366 | } 367 | int y = father[x]; 368 | father[x] = get(y); 369 | dist[x] += dist[y]; 370 | return father[x]; 371 | } 372 | ``` 373 | 374 | 375 | 376 | #### 提示 377 | ```c++ 378 | #include 379 | using namespace std; 380 | int father[110], n; 381 | int dist[110], size[110]; 382 | 383 | void init() { 384 | for (int i = 1; i <= n; ++i) { 385 | father[i] = i, dist[i] = 0, size[i] = 1; 386 | } 387 | } 388 | int get(int x) { 389 | if (father[x] == x) { 390 | return x; 391 | } 392 | int y = father[x]; 393 | father[x] = get(y); 394 | dist[x] += dist[y]; 395 | return father[x]; 396 | } 397 | void merge(int a, int b) { 398 | a = get(a); 399 | b = get(b); 400 | if (a != b) { 401 | father[a] = b; 402 | dist[a] = size[b]; 403 | size[b] += size[a]; 404 | } 405 | } 406 | int main() { 407 | n = 10; 408 | init(); 409 | merge(1, 2); 410 | merge(10, 7); 411 | merge(3, 4); 412 | merge(3, 7); 413 | get(1); // 一定要先 get,可能没有直接连接根节点 414 | cout << dist[1] + 1 << endl; 415 | 416 | return 0; 417 | } 418 | ``` 419 | 420 | 421 | #### 代码 422 | ```c++ 423 | #include 424 | using namespace std; 425 | int father[110], n; 426 | int dist[110], size[110]; 427 | 428 | void init() { 429 | for (int i = 1; i <= n; ++i) { 430 | father[i] = i, dist[i] = 0, size[i] = 1; 431 | } 432 | } 433 | int get(int x) { 434 | if (father[x] == x) { 435 | return x; 436 | } 437 | int y = father[x]; 438 | father[x] = get(y); 439 | dist[x] += dist[y]; 440 | return father[x]; 441 | } 442 | void merge(int a, int b) { 443 | a = get(a); 444 | b = get(b); 445 | if (a != b) { 446 | father[a] = b; 447 | dist[a] = size[b]; 448 | size[b] += size[a]; 449 | } 450 | } 451 | int main() { 452 | n = 10; 453 | init(); 454 | merge(1, 2); 455 | merge(10, 7); 456 | merge(3, 4); 457 | merge(3, 7); 458 | get(1); // 一定要先 get,可能没有直接连接根节点 459 | cout << dist[1] + 1 << endl; 460 | 461 | return 0; 462 | } 463 | ``` 464 | 465 | 466 | 467 | --- 468 | ### 第八步 469 | #### 讲解 470 | 到这里,里可以点击**运行**看看效果。 471 | 472 | 接下来我们查找`3`所在集合的根节点并输出`3`到根节点的距离`dist`。在`main`函数里继续写下 473 | ```c++ 474 | get(3); 475 | cout << dist[3] + 1 << endl; 476 | ``` 477 | 478 | #### 提示 479 | ```c++ 480 | #include 481 | using namespace std; 482 | int father[110], n; 483 | int dist[110], size[110]; 484 | 485 | void init() { 486 | for (int i = 1; i <= n; ++i) { 487 | father[i] = i, dist[i] = 0, size[i] = 1; 488 | } 489 | } 490 | int get(int x) { 491 | if (father[x] == x) { 492 | return x; 493 | } 494 | int y = father[x]; 495 | father[x] = get(y); 496 | dist[x] += dist[y]; 497 | return father[x]; 498 | } 499 | void merge(int a, int b) { 500 | a = get(a); 501 | b = get(b); 502 | if (a != b) { 503 | father[a] = b; 504 | dist[a] = size[b]; 505 | size[b] += size[a]; 506 | } 507 | } 508 | int main() { 509 | n = 10; 510 | init(); 511 | merge(1, 2); 512 | merge(10, 7); 513 | merge(3, 4); 514 | merge(3, 7); 515 | get(1); // 一定要先 get,可能没有直接连接根节点 516 | cout << dist[1] + 1 << endl; 517 | get(3); 518 | cout << dist[3] + 1 << endl; 519 | return 0; 520 | } 521 | ``` 522 | 523 | 524 | #### 代码 525 | ```c++ 526 | #include 527 | using namespace std; 528 | int father[110], n; 529 | int dist[110], size[110]; 530 | 531 | void init() { 532 | for (int i = 1; i <= n; ++i) { 533 | father[i] = i, dist[i] = 0, size[i] = 1; 534 | } 535 | } 536 | int get(int x) { 537 | if (father[x] == x) { 538 | return x; 539 | } 540 | int y = father[x]; 541 | father[x] = get(y); 542 | dist[x] += dist[y]; 543 | return father[x]; 544 | } 545 | void merge(int a, int b) { 546 | a = get(a); 547 | b = get(b); 548 | if (a != b) { 549 | father[a] = b; 550 | dist[a] = size[b]; 551 | size[b] += size[a]; 552 | } 553 | } 554 | int main() { 555 | n = 10; 556 | init(); 557 | merge(1, 2); 558 | merge(10, 7); 559 | merge(3, 4); 560 | merge(3, 7); 561 | get(1); // 一定要先 get,可能没有直接连接根节点 562 | cout << dist[1] + 1 << endl; 563 | get(3); 564 | cout << dist[3] + 1 << endl; 565 | return 0; 566 | } 567 | ``` 568 | 569 | 570 | 571 | 572 | --- 573 | ### 完成讲解 574 | 这一节已经完成了,赶紧运行看看效果。 575 | 576 | 聪明的你一定学会了`disjoint`怎么用了。 577 | -------------------------------------------------------------------------------- /1_datastructure/code/1_vector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | int main() { 5 | vector v; 6 | for (int i = 1; i <= 10; ++i) { 7 | v.push_back(i * i); 8 | } 9 | for (int i = 0; i < v.size(); ++i) { 10 | cout << v[i] << " "; 11 | } 12 | cout << endl; 13 | vector > v2d; 14 | for (int i = 0; i < 5; ++i) { 15 | v2d.push_back(vector()); 16 | } 17 | for (int i = 0; i < v2d.size(); ++i) { 18 | for (int j = 0; j < 5; ++j) { 19 | v2d[i].push_back(i * j); 20 | } 21 | } 22 | for (int i = 0; i < v2d.size(); ++i) { 23 | for (int j = 0; j < v2d[i].size(); ++j) { 24 | cout << v2d[i][j] << " "; 25 | } 26 | cout << endl; 27 | } 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /1_datastructure/code/2_set.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | int main() { 5 | set country; 6 | country.insert("China"); 7 | country.insert("America"); 8 | country.insert("France"); 9 | set::iterator it; 10 | for (it = country.begin(); it != country.end(); ++it) { 11 | cout << * it << " "; 12 | } 13 | cout << endl; 14 | country.erase("American"); 15 | country.erase("England"); 16 | if (country.count("China")) { 17 | cout << "China in country." << endl; 18 | } 19 | country.clear(); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /1_datastructure/code/3_map.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | int main() { 6 | map dict; 7 | dict["Tom"] = 1; 8 | dict["Jone"] = 2; 9 | dict["Mary"] = 1; 10 | if (dict.count("Mary")) { 11 | cout << "Mary is in class " << dict["Mary"]; 12 | } 13 | for (map::iterator it = dict.begin(); it != dict.end(); ++it) { 14 | cout << it->first << " is in class " << it->second << endl; 15 | } 16 | dict.clear(); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /1_datastructure/code/4_stack.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | stack S; 5 | int main() { 6 | S.push(1); 7 | S.push(10); 8 | S.push(7); 9 | while (!S.empty()) { 10 | cout << S.top() << endl; 11 | S.pop(); 12 | } 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /1_datastructure/code/5_queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | int main() { 5 | queue q; 6 | q.push(1); 7 | q.push(2); 8 | q.push(3); 9 | while (!q.empty()) { 10 | cout << q.front() << endl; 11 | q.pop(); 12 | } 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /1_datastructure/code/6_priority_queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | int main() { 5 | priority_queue q; 6 | q.push(1); 7 | q.push(2); 8 | q.push(3); 9 | while (!q.empty()) { 10 | cout << q.top() << endl; 11 | q.pop(); 12 | } 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /1_datastructure/code/7_disjoint.cpp: -------------------------------------------------------------------------------- 1 | // 思路是,先讲普通的并查集,然后扩展都带权并查集 2 | // 题目背景是 https://www.jisuanke.com/course/720/37707, 带权并查集部分。 3 | 4 | 5 | #include 6 | using namespace std; 7 | int father[110], n; 8 | int dist[110], size[110]; 9 | 10 | void init() { 11 | for (int i = 1; i <= n; ++i) { 12 | father[i] = i, dist[i] = 0, size[i] = 1; 13 | } 14 | } 15 | int get(int x) { 16 | if (father[x] == x) { 17 | return x; 18 | } 19 | int y = father[x]; 20 | father[x] = get(y); 21 | dist[x] += dist[y]; 22 | return father[x]; 23 | } 24 | void merge(int a, int b) { 25 | a = get(a); 26 | b = get(b); 27 | if (a != b) { 28 | father[a] = b; 29 | dist[a] = size[b]; 30 | size[b] += size[a]; 31 | } 32 | } 33 | int main() { 34 | n = 10; 35 | init(); 36 | merge(1, 2); 37 | merge(10, 7); 38 | merge(3, 4); 39 | merge(3, 7); 40 | get(1); // 一定要先 get,可能没有直接连接根节点 41 | cout << dist[1] + 1 << endl; 42 | get(3); 43 | cout << dist[3] + 1 << endl; 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /1_datastructure/content.md: -------------------------------------------------------------------------------- 1 | # 基础数据结构模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|动态数组的使用|1|| 7 | |跟随|集合的使用|1|| 8 | |跟随|映射表的使用|1|| 9 | |跟随|栈的使用|1|| 10 | |跟随|队列的使用|1|| 11 | |跟随|优先队列的使用|1|| 12 | |跟随|并查集的使用|1|| 13 | 14 | 基础数据结构。 15 | -------------------------------------------------------------------------------- /1_datastructure/优先队列.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verticallimit/Magic-Algorithm/cb73858d37f084c3eb5e9d9096909eb11f138a33/1_datastructure/优先队列.cpp -------------------------------------------------------------------------------- /1_datastructure/优先队列.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verticallimit/Magic-Algorithm/cb73858d37f084c3eb5e9d9096909eb11f138a33/1_datastructure/优先队列.md -------------------------------------------------------------------------------- /1_datastructure/单调队列.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verticallimit/Magic-Algorithm/cb73858d37f084c3eb5e9d9096909eb11f138a33/1_datastructure/单调队列.cpp -------------------------------------------------------------------------------- /1_datastructure/单调队列.md: -------------------------------------------------------------------------------- 1 | # `单调队列` 2 | 3 | ### 简介 4 | 单调队列,即单调递减或单调递增的队列。使用频率不高,但在有些程序中会有非同寻常的作用。 5 | 6 | 7 | - 重要特性:元素可以从两端进出 8 | 9 | 实现方式 10 | - 数组 11 | - 链表 12 | - STL 13 | 14 | deque的性质 15 | - 容器中间插入和删除效率很低 16 | - 与vector容器操作类似 17 | 18 | 19 | ### 例题 20 | - 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4286 21 | 22 | 题意:n个数字一开始排成一行,有两个指针L和R,指向了这串数字的一个区间的端点。 23 | 现在有7种操作 24 | - 使一个指针左移 25 | - 使一个指针右移 26 | - 在L后插入一个数字X 27 | - 在R前插入一个数字X 28 | - 删除L所指元素 29 | - 删除R所指元素 30 | - 翻转[L,R]区间 31 | 32 | Question:m次操作后,整串数字是什么样的? 33 | 34 | 分析:如果区间L和R每一次都是任意范围的,那么肯定是平衡树问题。但是现在区间断点L和R每次只能左移一位或右移一位,那么可以将整串数字以L、R为中点分为三段,然后模拟这几种操作。但是翻转不能直接模拟,用一个标记来判断一下正反。 35 | -------------------------------------------------------------------------------- /1_datastructure/双端队列.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int main() { 7 | deque dq; 8 | dq.push_back(1); 9 | dq.push_back(2); 10 | dq.push_back(3); 11 | dq.push_front(4); 12 | dq.push_front(5); 13 | dq.push_front(6); 14 | dq.pop_back(); 15 | dq.pop_front(); 16 | deque::iterator it; 17 | for (it = dq.begin(); it != dq.end(); it++) { 18 | cout << *it << " "; 19 | } 20 | cout << endl; 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /1_datastructure/双端队列.md: -------------------------------------------------------------------------------- 1 | # `双端队列` 2 | 3 | ### 简介 4 | 双端队列(deque,全名double-ended queue)是一个限定插入和删除操作的数据结构,具有队列和栈的性质。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。 5 | 双端队列是限定插入和删除操作在表的两端进行的线性表。这两端分别称做端点1和端点2。也可像栈一样,可以用一个铁道转轨网络来比喻双端队列。 6 | 7 | 8 | - 重要特性:元素可以从两端进出 9 | 10 | 实现方式 11 | - 数组 12 | - 链表 13 | - STL 14 | 15 | deque的性质 16 | - 容器中间插入和删除效率很低 17 | - 与vector容器操作类似 18 | 19 | 20 | ### 例题 21 | - 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4286 22 | 23 | 题意:n个数字一开始排成一行,有两个指针L和R,指向了这串数字的一个区间的端点。 24 | 现在有7种操作 25 | - 使一个指针左移 26 | - 使一个指针右移 27 | - 在L后插入一个数字X 28 | - 在R前插入一个数字X 29 | - 删除L所指元素 30 | - 删除R所指元素 31 | - 翻转[L,R]区间 32 | 33 | Question:m次操作后,整串数字是什么样的? 34 | 35 | 分析:如果区间L和R每一次都是任意范围的,那么肯定是平衡树问题。但是现在区间断点L和R每次只能左移一位或右移一位,那么可以将整串数字以L、R为中点分为三段,然后模拟这几种操作。但是翻转不能直接模拟,用一个标记来判断一下正反。 36 | -------------------------------------------------------------------------------- /1_datastructure/队列.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | struct Queue { 7 | /* 数组实现 */ 8 | void Queue_Array() { 9 | int q[1005] = {0}; 10 | int head = 0; 11 | int tail = head; 12 | q[tail++] = 1; 13 | q[tail++] = 2; 14 | q[tail++] = 3; 15 | while (head < tail) { 16 | printf("%d\n", q[head]); 17 | head++; 18 | } 19 | } 20 | 21 | /* STL实现 */ 22 | void Queue_STL() { 23 | queue q; 24 | q.push(1); 25 | q.push(2); 26 | q.push(3); 27 | while (!q.empty()) { 28 | printf("%d\n", q.front()); 29 | q.pop(); 30 | } 31 | } 32 | }Q; 33 | 34 | int main() { 35 | Q.Queue_Array(); 36 | Q.Queue_STL(); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /1_datastructure/队列.md: -------------------------------------------------------------------------------- 1 | # `队列` 2 | 3 | ### 简介 4 | 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 5 | 6 | - 重要特性:元素先进先出 7 | 8 | 9 | ### 实现方式 10 | - 数组 11 | - 链表 12 | - STL 13 | -------------------------------------------------------------------------------- /2_search/code/1_queen.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | int n; 7 | bool flag = 0; 8 | int G[20][20]; 9 | bool a[20]; // 列占用情况,若第 i 列被占用,则 a[i] = true,否则为 false 10 | bool x1[20]; // 左下-右上 对角线的占用情况 11 | bool y1[20]; // 左上-右下 对角线的占用情况 12 | void dfs(int deep) { 13 | if (flag == 1) { 14 | return; 15 | } 16 | if (deep >= n) { 17 | for (int i = 0; i < n; ++i) { 18 | for (int j = 0; j < n; ++j) { 19 | cout << G[i][j] << " "; 20 | } 21 | cout << endl; 22 | } 23 | flag = 1; 24 | return; 25 | } 26 | for (int i = 0; i < n; i++) { 27 | if (x1[i + deep] == false && y1[i - deep + n] == false && a[i] == false) { 28 | x1[deep + i] = true; 29 | y1[i - deep + n] = true; 30 | a[i] = true; 31 | G[deep][i] = 1; 32 | dfs(deep + 1); 33 | a[i] = false; 34 | x1[deep + i]=false; 35 | y1[i - deep + n] = false; 36 | G[deep][i] = 0; 37 | } 38 | } 39 | } 40 | int main() { 41 | cin >> n; 42 | memset(a, false, sizeof(a)); 43 | memset(x1, false, sizeof(x1)); 44 | memset(y1, false, sizeof(y1)); 45 | memset(G, 0, sizeof(G)); 46 | dfs(0); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /2_search/code/2_bfs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | struct point { 6 | int x, y, step; 7 | point(int _x, int _y, int _step): 8 | x(_x), y(_y), step(_step) {} 9 | }; 10 | 11 | int n, m; 12 | char maze[110][110]; 13 | bool vis[110][110]; 14 | int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0}; 15 | 16 | bool in(int tx, int ty) { 17 | return 0 <= tx && tx < n && 0 <= ty && ty < m; 18 | } 19 | 20 | int bfs(int sx, int sy) { 21 | queue q; 22 | q.push(point(sx, sy, 0)); 23 | vis[sx][sy] = 1; 24 | while (!q.empty()) { 25 | int x = q.front().x; 26 | int y = q.front().y; 27 | int step = q.front().step; 28 | q.pop(); 29 | for (int i = 0; i < 4; ++i) { 30 | int tx = x + dir[i][0]; 31 | int ty = y + dir[i][1]; 32 | if (in(tx, ty) && maze[tx][ty] != '#' && !vis[tx][ty]) { 33 | if (maze[tx][ty] == 'T') { 34 | return step + 1; 35 | } 36 | q.push(point(tx, ty, step + 1)); 37 | vis[tx][ty] = 1; 38 | } 39 | } 40 | } 41 | return -1; 42 | } 43 | 44 | 45 | int main() { 46 | int sx, sy; 47 | scanf("%d %d", &n, &m); 48 | for (int i = 0; i < n; ++i) { 49 | scanf("%s", maze[i]); 50 | for (int j = 0; j < m; ++j) { 51 | if (maze[i][j] == 'S') { 52 | sx = i, sy = j; 53 | } 54 | } 55 | } 56 | printf("%d\n", bfs(sx, sy)); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /2_search/content.md: -------------------------------------------------------------------------------- 1 | # 搜索模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|八皇后问题求解|1|| 7 | |跟随|bfs 模板|1|| 8 | 9 | 10 | 搜索。 11 | -------------------------------------------------------------------------------- /3_dp/1_knapsack.md: -------------------------------------------------------------------------------- 1 | # knapsack 模板 2 | - knapsack 3 | 4 | ### 文件名 5 | knapsack.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | 11 | --- 12 | ### 初始化代码 13 | ```c++ 14 | // 01 背包 15 | #include 16 | #include 17 | using namespace std; 18 | 19 | int main() { 20 | 21 | return 0; 22 | } 23 | ``` 24 | 25 | --- 26 | ### 第一步 27 | #### 讲解 28 | 通过本节课,我们用动态规划算法来解决一个实际问题——01背包问题。 29 | 30 | 01背包问题是一个经典的问题,给定N个物品和一个背包。物品i的重量是Wi,其体积为Ci ,背包的容量为C。问应该如何选择装入背包的物品,使得装入背包的物品的总重量为最大 31 | 32 | 我们先定义一些标记数组。在`main`函数的上方写下 33 | 34 | ```c++ 35 | int dp[21][1010]; 36 | int w[21], c[21]; 37 | ``` 38 | 39 | #### 提示 40 | ```c++ 41 | // 01 背包 42 | #include 43 | #include 44 | using namespace std; 45 | 46 | int dp[21][1010]; 47 | int w[21], c[21]; 48 | 49 | int main() { 50 | 51 | return 0; 52 | } 53 | ``` 54 | 55 | #### 代码 56 | ```c++ 57 | // 01 背包 58 | #include 59 | #include 60 | using namespace std; 61 | 62 | int dp[21][1010]; 63 | int w[21], c[21]; 64 | 65 | int main() { 66 | 67 | return 0; 68 | } 69 | ``` 70 | 71 | 72 | --- 73 | ### 第二步 74 | #### 讲解 75 | 下面我们要输入物品的个数N和背包的体积V。在`main`函数里写下 76 | 77 | ```c++ 78 | int N, V; 79 | cin >> N >> V; 80 | ``` 81 | 82 | 83 | 84 | #### 提示 85 | ```c++ 86 | // 01 背包 87 | #include 88 | #include 89 | using namespace std; 90 | 91 | int dp[21][1010]; 92 | int w[21], c[21]; 93 | 94 | int main() { 95 | int N, V; 96 | cin >> N >> V; 97 | 98 | return 0; 99 | } 100 | ``` 101 | 102 | 103 | #### 代码 104 | ```c++ 105 | // 01 背包 106 | #include 107 | #include 108 | using namespace std; 109 | 110 | int dp[21][1010]; 111 | int w[21], c[21]; 112 | 113 | int main() { 114 | int N, V; 115 | cin >> N >> V; 116 | 117 | return 0; 118 | } 119 | ``` 120 | 121 | --- 122 | ### 第三步 123 | #### 讲解 124 | 接下来我们继续输入N个物品各自的重量和体积。在`main`函数里面继续写下 125 | ```c++ 126 | for (int i = 1; i <= N; ++i) { 127 | cin >> w[i] >> c[i]; 128 | } 129 | ``` 130 | 131 | #### 提示 132 | ```c++ 133 | // 01 背包 134 | #include 135 | #include 136 | using namespace std; 137 | 138 | int dp[21][1010]; 139 | int w[21], c[21]; 140 | 141 | int main() { 142 | int N, V; 143 | cin >> N >> V; 144 | for (int i = 1; i <= N; ++i) { 145 | cin >> w[i] >> c[i]; 146 | } 147 | 148 | return 0; 149 | } 150 | ``` 151 | 152 | 153 | #### 代码 154 | ```c++ 155 | // 01 背包 156 | #include 157 | #include 158 | using namespace std; 159 | 160 | int dp[21][1010]; 161 | int w[21], c[21]; 162 | 163 | int main() { 164 | int N, V; 165 | cin >> N >> V; 166 | for (int i = 1; i <= N; ++i) { 167 | cin >> w[i] >> c[i]; 168 | } 169 | 170 | return 0; 171 | } 172 | ``` 173 | 174 | 175 | --- 176 | ### 第四步 177 | #### 讲解 178 | 接下来是最重要的的一步,对于一个动态规划来说,最重要的是找到状态转移方程。 179 | 在01背包问题中,一个物品要么装要么不装,那么我们可以得出下面的式子 180 | f[i,j]代表前i个物品背包容量最大为j最多能装的物品总重量 181 | f[i,j] = Max{ f[i-1,j-Ci]+Wi( j >= Ci ), f[i-1,j] } 182 | 根据上面的状态转移方程可以写出下面的代码 183 | ```c++ 184 | for (int i = 1; i <= N; ++i) { 185 | for (int j = 0; j <= V; ++j) { 186 | if(j >= c[i]) { 187 | dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]); 188 | } 189 | else { 190 | dp[i][j] = dp[i-1][j]; 191 | } 192 | } 193 | } 194 | ``` 195 | 196 | #### 提示 197 | ```c++ 198 | // 01 背包 199 | #include 200 | #include 201 | using namespace std; 202 | 203 | int dp[21][1010]; 204 | int w[21], c[21]; 205 | 206 | int main() { 207 | int N, V; 208 | cin >> N >> V; 209 | for (int i = 1; i <= N; ++i) { 210 | cin >> w[i] >> c[i]; 211 | } 212 | for (int i = 1; i <= N; ++i) { 213 | for (int j = 0; j <= V; ++j) { 214 | if(j >= c[i]) { 215 | dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]); 216 | } 217 | else { 218 | dp[i][j] = dp[i-1][j]; 219 | } 220 | } 221 | } 222 | return 0; 223 | } 224 | ``` 225 | 226 | #### 代码 227 | ```c++ 228 | // 01 背包 229 | #include 230 | #include 231 | using namespace std; 232 | 233 | int dp[21][1010]; 234 | int w[21], c[21]; 235 | 236 | int main() { 237 | int N, V; 238 | cin >> N >> V; 239 | for (int i = 1; i <= N; ++i) { 240 | cin >> w[i] >> c[i]; 241 | } 242 | for (int i = 1; i <= N; ++i) { 243 | for (int j = 0; j <= V; ++j) { 244 | if(j >= c[i]) { 245 | dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]); 246 | } 247 | else { 248 | dp[i][j] = dp[i-1][j]; 249 | } 250 | } 251 | } 252 | return 0; 253 | } 254 | ``` 255 | 256 | --- 257 | ### 第五步 258 | #### 讲解 259 | 最后输出我们想要的结果。在`main`函数里继续写下 260 | ```c++ 261 | cout << dp[N][V] << endl; 262 | ``` 263 | 264 | #### 提示 265 | ```c++ 266 | // 01 背包 267 | #include 268 | #include 269 | using namespace std; 270 | 271 | int dp[21][1010]; 272 | int w[21], c[21]; 273 | 274 | int main() { 275 | int N, V; 276 | cin >> N >> V; 277 | for (int i = 1; i <= N; ++i) { 278 | cin >> w[i] >> c[i]; 279 | } 280 | for (int i = 1; i <= N; ++i) { 281 | for (int j = 0; j <= V; ++j) { 282 | if(j >= c[i]) { 283 | dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]); 284 | } 285 | else { 286 | dp[i][j] = dp[i-1][j]; 287 | } 288 | } 289 | } 290 | cout << dp[N][V] << endl; 291 | return 0; 292 | } 293 | ``` 294 | 295 | #### 代码 296 | ```c++ 297 | // 01 背包 298 | #include 299 | #include 300 | using namespace std; 301 | 302 | int dp[21][1010]; 303 | int w[21], c[21]; 304 | 305 | int main() { 306 | int N, V; 307 | cin >> N >> V; 308 | for (int i = 1; i <= N; ++i) { 309 | cin >> w[i] >> c[i]; 310 | } 311 | for (int i = 1; i <= N; ++i) { 312 | for (int j = 0; j <= V; ++j) { 313 | if(j >= c[i]) { 314 | dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]); 315 | } 316 | else { 317 | dp[i][j] = dp[i-1][j]; 318 | } 319 | } 320 | } 321 | cout << dp[N][V] << endl; 322 | return 0; 323 | } 324 | ``` 325 | 326 | 327 | --- 328 | ### 完成讲解 329 | 终于完成了,点击运行,输入下面的数据看看效果吧。 330 | ``` 331 | 5 10 332 | 2 1 333 | 3 5 334 | 2 5 335 | 3 4 336 | 4 3 337 | ``` 338 | -------------------------------------------------------------------------------- /3_dp/2_LIS.md: -------------------------------------------------------------------------------- 1 | # LIS 模板 2 | - LIS 3 | 4 | ### 文件名 5 | LIS.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | 11 | --- 12 | ### 初始化代码 13 | ```c++ 14 | // n^2 和 nlgn 的最长上升子序列 15 | 16 | #include 17 | #include 18 | 19 | using namespace std; 20 | 21 | int main() { 22 | 23 | return 0; 24 | } 25 | ``` 26 | 27 | --- 28 | ### 第一步 29 | #### 讲解 30 | 通过本节课,我们用动态规划算法来解决一个实际问题——最长上升子序列问题。 31 | 32 | 最长上升子序列(Longest Increasing Subsequence, 简称LIS)是dp中比较经典的一个算法模型, 它有一种朴素的算法O(n^2)和一种优化版的算法O(nlogn)实现, 通过它, 我们可以进一步了解dp的思想. 33 | 34 | 我们先定义一些辅助数组。在`main`函数的上方写下 35 | 36 | ```c++ 37 | int dp[101], a[101], n; 38 | ``` 39 | 40 | #### 提示 41 | ```c++ 42 | // n^2 和 nlgn 的最长上升子序列 43 | 44 | #include 45 | #include 46 | 47 | using namespace std; 48 | int dp[101], a[101], n; 49 | 50 | int main() { 51 | 52 | return 0; 53 | } 54 | ``` 55 | 56 | #### 代码 57 | ```c++ 58 | // n^2 和 nlgn 的最长上升子序列 59 | 60 | #include 61 | #include 62 | 63 | using namespace std; 64 | int dp[101], a[101], n; 65 | 66 | int main() { 67 | 68 | return 0; 69 | } 70 | ``` 71 | 72 | 73 | --- 74 | ### 第二步 75 | #### 讲解 76 | 下面我们要输入总个数N和N个数。在`main`函数里写下 77 | 78 | ```c++ 79 | cin >> n; 80 | for (int i = 1; i <= n; ++i) { 81 | cin >> a[i]; 82 | } 83 | ``` 84 | 85 | 86 | 87 | #### 提示 88 | ```c++ 89 | // n^2 和 nlgn 的最长上升子序列 90 | 91 | #include 92 | #include 93 | 94 | using namespace std; 95 | int dp[101], a[101], n; 96 | 97 | int main() { 98 | cin >> n; 99 | for (int i = 1; i <= n; ++i) { 100 | cin >> a[i]; 101 | } 102 | 103 | return 0; 104 | } 105 | ``` 106 | 107 | 108 | #### 代码 109 | ```c++ 110 | // n^2 和 nlgn 的最长上升子序列 111 | 112 | #include 113 | #include 114 | 115 | using namespace std; 116 | int dp[101], a[101], n; 117 | 118 | int main() { 119 | cin >> n; 120 | for (int i = 1; i <= n; ++i) { 121 | cin >> a[i]; 122 | } 123 | 124 | return 0; 125 | } 126 | ``` 127 | 128 | --- 129 | ### 第三步 130 | #### 讲解 131 | 接下来我们实现O(n^2)的算法`LIS_nn()`。 132 | 首先确定状态转移方程 133 | dp[i]代表以第i项为结尾的LIS的长度 134 | dp[i] = max(dp[i], max(dp[j]) + 1) if j < i and a[j] < a[i] 135 | 根据上面的状态转移方程可以写出下面的代码 136 | ```c++ 137 | int LIS_nn() { 138 | int ans = 0; 139 | for (int i = 1; i <= n; ++i) { 140 | dp[i] = 1; 141 | for (int j = 1; j < i; ++j) { 142 | if (a[j] < a[i]) { 143 | dp[i] = max(dp[i], dp[j] + 1); 144 | } 145 | } 146 | ans = max(ans, dp[i]); 147 | } 148 | return ans; 149 | } 150 | ``` 151 | 152 | #### 提示 153 | ```c++ 154 | // n^2 和 nlgn 的最长上升子序列 155 | 156 | #include 157 | #include 158 | 159 | using namespace std; 160 | int dp[101], a[101], n; 161 | 162 | int LIS_nn() { 163 | int ans = 0; 164 | for (int i = 1; i <= n; ++i) { 165 | dp[i] = 1; 166 | for (int j = 1; j < i; ++j) { 167 | if (a[j] < a[i]) { 168 | dp[i] = max(dp[i], dp[j] + 1); 169 | } 170 | } 171 | ans = max(ans, dp[i]); 172 | } 173 | return ans; 174 | } 175 | 176 | int main() { 177 | cin >> n; 178 | for (int i = 1; i <= n; ++i) { 179 | cin >> a[i]; 180 | } 181 | 182 | return 0; 183 | } 184 | ``` 185 | 186 | 187 | #### 代码 188 | ```c++ 189 | // n^2 和 nlgn 的最长上升子序列 190 | 191 | #include 192 | #include 193 | 194 | using namespace std; 195 | int dp[101], a[101], n; 196 | 197 | int LIS_nn() { 198 | int ans = 0; 199 | for (int i = 1; i <= n; ++i) { 200 | dp[i] = 1; 201 | for (int j = 1; j < i; ++j) { 202 | if (a[j] < a[i]) { 203 | dp[i] = max(dp[i], dp[j] + 1); 204 | } 205 | } 206 | ans = max(ans, dp[i]); 207 | } 208 | return ans; 209 | } 210 | 211 | int main() { 212 | cin >> n; 213 | for (int i = 1; i <= n; ++i) { 214 | cin >> a[i]; 215 | } 216 | 217 | return 0; 218 | } 219 | ``` 220 | 221 | 222 | --- 223 | ### 第四步 224 | #### 讲解 225 | 接下来输出刚才算法的结果。在`main`函数里继续写下 226 | ```c++ 227 | cout << LIS_nn() << endl; 228 | ``` 229 | 230 | #### 提示 231 | ```c++ 232 | // n^2 和 nlgn 的最长上升子序列 233 | 234 | #include 235 | #include 236 | 237 | using namespace std; 238 | int dp[101], a[101], n; 239 | 240 | int LIS_nn() { 241 | int ans = 0; 242 | for (int i = 1; i <= n; ++i) { 243 | dp[i] = 1; 244 | for (int j = 1; j < i; ++j) { 245 | if (a[j] < a[i]) { 246 | dp[i] = max(dp[i], dp[j] + 1); 247 | } 248 | } 249 | ans = max(ans, dp[i]); 250 | } 251 | return ans; 252 | } 253 | 254 | int main() { 255 | cin >> n; 256 | for (int i = 1; i <= n; ++i) { 257 | cin >> a[i]; 258 | } 259 | cout << LIS_nn() << endl; 260 | 261 | return 0; 262 | } 263 | ``` 264 | 265 | #### 代码 266 | ```c++ 267 | // n^2 和 nlgn 的最长上升子序列 268 | 269 | #include 270 | #include 271 | 272 | using namespace std; 273 | int dp[101], a[101], n; 274 | 275 | int LIS_nn() { 276 | int ans = 0; 277 | for (int i = 1; i <= n; ++i) { 278 | dp[i] = 1; 279 | for (int j = 1; j < i; ++j) { 280 | if (a[j] < a[i]) { 281 | dp[i] = max(dp[i], dp[j] + 1); 282 | } 283 | } 284 | ans = max(ans, dp[i]); 285 | } 286 | return ans; 287 | } 288 | 289 | int main() { 290 | cin >> n; 291 | for (int i = 1; i <= n; ++i) { 292 | cin >> a[i]; 293 | } 294 | cout << LIS_nn() << endl; 295 | 296 | return 0; 297 | } 298 | ``` 299 | 300 | --- 301 | ### 第五步 302 | #### 讲解 303 | 我们继续思考一下刚才的算法是否还有优化的空间呢? 304 | 在刚才的内层for我们从前往后找一个最大的LIS值,仔细想一下是否可以发现这个值一定是单调递增的呢? 305 | 由于这个值是单调递增的,所以我们就没必要使用从前往后遍历的方法,可以使用二分查找来优化这个寻找的过程。 306 | 于是可以实现O(nlogn)算法的`LIS_nlgn()`函数。 307 | 在`main`函数上面继续写下 308 | ```c++ 309 | int LIS_nlgn() { 310 | int len = 1; 311 | dp[1] = a[1]; 312 | 313 | for (int i = 2; i <= n; ++i) { 314 | if (a[i] > dp[len]) { 315 | dp[++len] = a[i]; 316 | } else { 317 | int pos = lower_bound(dp, dp + len, a[i]) - dp; 318 | dp[pos] = a[i]; 319 | } 320 | } 321 | return len; 322 | } 323 | ``` 324 | 325 | #### 提示 326 | ```c++ 327 | // n^2 和 nlgn 的最长上升子序列 328 | 329 | #include 330 | #include 331 | 332 | using namespace std; 333 | int dp[101], a[101], n; 334 | 335 | int LIS_nn() { 336 | int ans = 0; 337 | for (int i = 1; i <= n; ++i) { 338 | dp[i] = 1; 339 | for (int j = 1; j < i; ++j) { 340 | if (a[j] < a[i]) { 341 | dp[i] = max(dp[i], dp[j] + 1); 342 | } 343 | } 344 | ans = max(ans, dp[i]); 345 | } 346 | return ans; 347 | } 348 | 349 | int LIS_nlgn() { 350 | int len = 1; 351 | dp[1] = a[1]; 352 | 353 | for (int i = 2; i <= n; ++i) { 354 | if (a[i] > dp[len]) { 355 | dp[++len] = a[i]; 356 | } else { 357 | int pos = lower_bound(dp, dp + len, a[i]) - dp; 358 | dp[pos] = a[i]; 359 | } 360 | } 361 | return len; 362 | } 363 | 364 | int main() { 365 | cin >> n; 366 | for (int i = 1; i <= n; ++i) { 367 | cin >> a[i]; 368 | } 369 | cout << LIS_nn() << endl; 370 | 371 | return 0; 372 | } 373 | ``` 374 | 375 | #### 代码 376 | ```c++ 377 | // n^2 和 nlgn 的最长上升子序列 378 | 379 | #include 380 | #include 381 | 382 | using namespace std; 383 | int dp[101], a[101], n; 384 | 385 | int LIS_nn() { 386 | int ans = 0; 387 | for (int i = 1; i <= n; ++i) { 388 | dp[i] = 1; 389 | for (int j = 1; j < i; ++j) { 390 | if (a[j] < a[i]) { 391 | dp[i] = max(dp[i], dp[j] + 1); 392 | } 393 | } 394 | ans = max(ans, dp[i]); 395 | } 396 | return ans; 397 | } 398 | 399 | int LIS_nlgn() { 400 | int len = 1; 401 | dp[1] = a[1]; 402 | 403 | for (int i = 2; i <= n; ++i) { 404 | if (a[i] > dp[len]) { 405 | dp[++len] = a[i]; 406 | } else { 407 | int pos = lower_bound(dp, dp + len, a[i]) - dp; 408 | dp[pos] = a[i]; 409 | } 410 | } 411 | return len; 412 | } 413 | 414 | int main() { 415 | cin >> n; 416 | for (int i = 1; i <= n; ++i) { 417 | cin >> a[i]; 418 | } 419 | cout << LIS_nn() << endl; 420 | 421 | return 0; 422 | } 423 | ``` 424 | 425 | 426 | --- 427 | ### 第六步 428 | #### 讲解 429 | 接下来输出刚才算法的结果。在`main`函数里继续写下 430 | ```c++ 431 | cout << LIS_nlgn() << endl; 432 | ``` 433 | 434 | #### 提示 435 | ```c++ 436 | // n^2 和 nlgn 的最长上升子序列 437 | 438 | #include 439 | #include 440 | 441 | using namespace std; 442 | int dp[101], a[101], n; 443 | 444 | int LIS_nn() { 445 | int ans = 0; 446 | for (int i = 1; i <= n; ++i) { 447 | dp[i] = 1; 448 | for (int j = 1; j < i; ++j) { 449 | if (a[j] < a[i]) { 450 | dp[i] = max(dp[i], dp[j] + 1); 451 | } 452 | } 453 | ans = max(ans, dp[i]); 454 | } 455 | return ans; 456 | } 457 | 458 | int LIS_nlgn() { 459 | int len = 1; 460 | dp[1] = a[1]; 461 | 462 | for (int i = 2; i <= n; ++i) { 463 | if (a[i] > dp[len]) { 464 | dp[++len] = a[i]; 465 | } else { 466 | int pos = lower_bound(dp, dp + len, a[i]) - dp; 467 | dp[pos] = a[i]; 468 | } 469 | } 470 | return len; 471 | } 472 | 473 | 474 | int main() { 475 | cin >> n; 476 | for (int i = 1; i <= n; ++i) { 477 | cin >> a[i]; 478 | } 479 | cout << LIS_nn() << endl; 480 | cout << LIS_nlgn() << endl; 481 | return 0; 482 | } 483 | ``` 484 | 485 | #### 代码 486 | ```c++ 487 | // n^2 和 nlgn 的最长上升子序列 488 | 489 | #include 490 | #include 491 | 492 | using namespace std; 493 | int dp[101], a[101], n; 494 | 495 | int LIS_nn() { 496 | int ans = 0; 497 | for (int i = 1; i <= n; ++i) { 498 | dp[i] = 1; 499 | for (int j = 1; j < i; ++j) { 500 | if (a[j] < a[i]) { 501 | dp[i] = max(dp[i], dp[j] + 1); 502 | } 503 | } 504 | ans = max(ans, dp[i]); 505 | } 506 | return ans; 507 | } 508 | 509 | int LIS_nlgn() { 510 | int len = 1; 511 | dp[1] = a[1]; 512 | 513 | for (int i = 2; i <= n; ++i) { 514 | if (a[i] > dp[len]) { 515 | dp[++len] = a[i]; 516 | } else { 517 | int pos = lower_bound(dp, dp + len, a[i]) - dp; 518 | dp[pos] = a[i]; 519 | } 520 | } 521 | return len; 522 | } 523 | 524 | 525 | int main() { 526 | cin >> n; 527 | for (int i = 1; i <= n; ++i) { 528 | cin >> a[i]; 529 | } 530 | cout << LIS_nn() << endl; 531 | cout << LIS_nlgn() << endl; 532 | return 0; 533 | } 534 | ``` 535 | 536 | --- 537 | ### 完成讲解 538 | 终于完成了,点击运行,输入下面的数据看看效果吧。 539 | ``` 540 | 6 541 | 3 2 6 1 4 5 542 | ``` 543 | -------------------------------------------------------------------------------- /3_dp/3_LCS.md: -------------------------------------------------------------------------------- 1 | # LCS 模板 2 | - LCS 3 | 4 | ### 文件名 5 | LCS.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | 11 | --- 12 | ### 初始化代码 13 | ```c++ 14 | #include 15 | #include 16 | #include 17 | using namespace std; 18 | 19 | int main() { 20 | 21 | return 0; 22 | } 23 | ``` 24 | 25 | --- 26 | ### 第一步 27 | #### 讲解 28 | 通过本节课,我们用动态规划算法来解决一个实际问题——最长公共子序列问题。 29 | 30 | 最长公共子序列(LCS)是一个在一个序列集合中(通常为两个序列)用来查找所有序列中最长子序列的问题。一个数列 ,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则称为已知序列的最长公共子序列。 31 | 32 | 我们先定义一个辅助数组。在`main`函数的上方写下 33 | 34 | ```c++ 35 | int dp[110][110]; 36 | ``` 37 | 38 | #### 提示 39 | ```c++ 40 | #include 41 | #include 42 | #include 43 | using namespace std; 44 | int dp[110][110]; 45 | int main() { 46 | 47 | return 0; 48 | } 49 | ``` 50 | 51 | #### 代码 52 | ```c++ 53 | #include 54 | #include 55 | #include 56 | using namespace std; 57 | int dp[110][110]; 58 | int main() { 59 | 60 | return 0; 61 | } 62 | ``` 63 | 64 | 65 | --- 66 | ### 第二步 67 | #### 讲解 68 | 下面我们要输入两个字符串`a`和`b`并将`dp`数组初始化。在`main`函数里写下 69 | 70 | ```c++ 71 | string a, b; 72 | memset(dp, 0, sizeof(dp)); 73 | cin >> a >> b; 74 | ``` 75 | 76 | 77 | 78 | #### 提示 79 | ```c++ 80 | #include 81 | #include 82 | #include 83 | using namespace std; 84 | int dp[110][110]; 85 | int main() { 86 | string a, b; 87 | memset(dp, 0, sizeof(dp)); 88 | cin >> a >> b; 89 | 90 | return 0; 91 | } 92 | ``` 93 | 94 | 95 | #### 代码 96 | ```c++ 97 | #include 98 | #include 99 | #include 100 | using namespace std; 101 | int dp[110][110]; 102 | int main() { 103 | string a, b; 104 | memset(dp, 0, sizeof(dp)); 105 | cin >> a >> b; 106 | 107 | return 0; 108 | } 109 | ``` 110 | 111 | --- 112 | ### 第三步 113 | #### 讲解 114 | 接下来我们计算出字符串`a`和`b`的长度。 115 | 继续写下 116 | ```c++ 117 | int lena = a.size(); 118 | int lenb = b.size(); 119 | ``` 120 | 121 | #### 提示 122 | ```c++ 123 | #include 124 | #include 125 | #include 126 | using namespace std; 127 | int dp[110][110]; 128 | int main() { 129 | string a, b; 130 | memset(dp, 0, sizeof(dp)); 131 | cin >> a >> b; 132 | int lena = a.size(); 133 | int lenb = b.size(); 134 | 135 | return 0; 136 | } 137 | ``` 138 | 139 | 140 | #### 代码 141 | ```c++ 142 | #include 143 | #include 144 | #include 145 | using namespace std; 146 | int dp[110][110]; 147 | int main() { 148 | string a, b; 149 | memset(dp, 0, sizeof(dp)); 150 | cin >> a >> b; 151 | int lena = a.size(); 152 | int lenb = b.size(); 153 | 154 | return 0; 155 | } 156 | ``` 157 | 158 | 159 | --- 160 | ### 第四步 161 | #### 讲解 162 | 接下来我们分析算法的状态转移方程。 163 | dp[i, j]代表a字符串前i个字符组成的子串和b字符串前j个字符组成的子串的LCS。 164 | 那么 165 | dp[i, j] = 0 if i = 0 or j = 0 166 | dp[i, j] = dp[i - 1, j - 1] + 1 if i, j > 0 and ai = bj 167 | dp[i, j] = max{dp[i, j - 1], dp[i - 1, j]} if i, j > 0 and ai != bj 168 | 根据上面的状态转移方程可以写出一下代码 169 | ```c++ 170 | for(int i = 1; i <= lena; ++i) { 171 | for (int j = 1; j <= lenb; ++j) { 172 | if(a[i - 1] == b[j - 1]) { 173 | dp[i][j] = dp[i - 1][j - 1] + 1; 174 | } else { 175 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 176 | } 177 | } 178 | } 179 | ``` 180 | 181 | #### 提示 182 | ```c++ 183 | #include 184 | #include 185 | #include 186 | using namespace std; 187 | int dp[110][110]; 188 | int main() { 189 | string a, b; 190 | memset(dp, 0, sizeof(dp)); 191 | cin >> a >> b; 192 | int lena = a.size(); 193 | int lenb = b.size(); 194 | for(int i = 1; i <= lena; ++i) { 195 | for (int j = 1; j <= lenb; ++j) { 196 | if(a[i - 1] == b[j - 1]) { 197 | dp[i][j] = dp[i - 1][j - 1] + 1; 198 | } else { 199 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 200 | } 201 | } 202 | } 203 | 204 | return 0; 205 | } 206 | ``` 207 | 208 | #### 代码 209 | ```c++ 210 | #include 211 | #include 212 | #include 213 | using namespace std; 214 | int dp[110][110]; 215 | int main() { 216 | string a, b; 217 | memset(dp, 0, sizeof(dp)); 218 | cin >> a >> b; 219 | int lena = a.size(); 220 | int lenb = b.size(); 221 | for(int i = 1; i <= lena; ++i) { 222 | for (int j = 1; j <= lenb; ++j) { 223 | if(a[i - 1] == b[j - 1]) { 224 | dp[i][j] = dp[i - 1][j - 1] + 1; 225 | } else { 226 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 227 | } 228 | } 229 | } 230 | 231 | return 0; 232 | } 233 | ``` 234 | 235 | --- 236 | ### 第五步 237 | #### 讲解 238 | 最后我们输出要求解的LCS值。在`main`函数里面继续写下 239 | ```c++ 240 | cout << dp[lena][lenb] << endl; 241 | ``` 242 | 243 | #### 提示 244 | ```c++ 245 | #include 246 | #include 247 | #include 248 | using namespace std; 249 | int dp[110][110]; 250 | int main() { 251 | string a, b; 252 | memset(dp, 0, sizeof(dp)); 253 | cin >> a >> b; 254 | int lena = a.size(); 255 | int lenb = b.size(); 256 | for(int i = 1; i <= lena; ++i) { 257 | for (int j = 1; j <= lenb; ++j) { 258 | if(a[i - 1] == b[j - 1]) { 259 | dp[i][j] = dp[i - 1][j - 1] + 1; 260 | } else { 261 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 262 | } 263 | } 264 | } 265 | cout << dp[lena][lenb] << endl; 266 | return 0; 267 | } 268 | ``` 269 | 270 | #### 代码 271 | ```c++ 272 | #include 273 | #include 274 | #include 275 | using namespace std; 276 | int dp[110][110]; 277 | int main() { 278 | string a, b; 279 | memset(dp, 0, sizeof(dp)); 280 | cin >> a >> b; 281 | int lena = a.size(); 282 | int lenb = b.size(); 283 | for(int i = 1; i <= lena; ++i) { 284 | for (int j = 1; j <= lenb; ++j) { 285 | if(a[i - 1] == b[j - 1]) { 286 | dp[i][j] = dp[i - 1][j - 1] + 1; 287 | } else { 288 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 289 | } 290 | } 291 | } 292 | cout << dp[lena][lenb] << endl; 293 | return 0; 294 | } 295 | ``` 296 | 297 | 298 | --- 299 | ### 完成讲解 300 | 终于完成了,点击运行,输入下面的数据看看效果吧。 301 | ``` 302 | abcdefgh 303 | acjlfabhh 304 | ``` 305 | -------------------------------------------------------------------------------- /3_dp/code/1_knapsack.cpp: -------------------------------------------------------------------------------- 1 | // 01 背包 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int dp[21][1010]; 7 | int w[21], c[21]; 8 | 9 | int main() { 10 | int N, V; 11 | cin >> N >> V; 12 | for (int i = 1; i <= N; ++i) { 13 | cin >> w[i] >> c[i]; 14 | } 15 | for (int i = 1; i <= N; ++i) { 16 | for (int j = 0; j <= V; ++j) { 17 | if(j >= c[i]) { 18 | dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]); 19 | } 20 | else { 21 | dp[i][j] = dp[i-1][j]; 22 | } 23 | } 24 | } 25 | cout << dp[N][V] << endl; 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /3_dp/code/2_LIS.cpp: -------------------------------------------------------------------------------- 1 | // n^2 和 nlgn 的最长上升子序列 2 | 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | int dp[101], a[101], n; 8 | 9 | int LIS_nn() { 10 | int ans = 0; 11 | for (int i = 1; i <= n; ++i) { 12 | dp[i] = 1; 13 | for (int j = 1; j < i; ++j) { 14 | if (a[j] < a[i]) { 15 | dp[i] = max(dp[i], dp[j] + 1); 16 | } 17 | } 18 | ans = max(ans, dp[i]); 19 | } 20 | return ans; 21 | } 22 | 23 | int LIS_nlgn() { 24 | int len = 1; 25 | dp[1] = a[1]; 26 | 27 | for (int i = 2; i <= n; ++i) { 28 | if (a[i] > dp[len]) { 29 | dp[++len] = a[i]; 30 | } else { 31 | int pos = lower_bound(dp, dp + len, a[i]) - dp; 32 | dp[pos] = a[i]; 33 | } 34 | } 35 | return len; 36 | } 37 | 38 | 39 | int main() { 40 | cin >> n; 41 | for (int i = 1; i <= n; ++i) { 42 | cin >> a[i]; 43 | } 44 | cout << LIS_nn() << endl; 45 | cout << LIS_nlgn() << endl; 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /3_dp/code/3_LCS.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | int dp[110][110]; 6 | int main() { 7 | string a, b; 8 | memset(dp, 0, sizeof(dp)); 9 | cin >> a >> b; 10 | int lena = a.size(); 11 | int lenb = b.size(); 12 | for(int i = 1; i <= lena; ++i) { 13 | for (int j = 1; j <= lenb; ++j) { 14 | if(a[i - 1] == b[j - 1]) { 15 | dp[i][j] = dp[i - 1][j - 1] + 1; 16 | } else { 17 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 18 | } 19 | } 20 | } 21 | cout << dp[lena][lenb] << endl; 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /3_dp/code/4_stage.cpp: -------------------------------------------------------------------------------- 1 | // 题目背景: https://www.jisuanke.com/course/720/37842 例题1 2 | 3 | #include 4 | #include 5 | using namespace std; 6 | const int MAX_N = 20; 7 | const int MAX_M = 20; 8 | int state[MAX_N + 1]; 9 | int dp[MAX_N + 1][1 << MAX_M]; 10 | 11 | bool not_intersect(int now, int prev) { 12 | return (now & prev) == 0; 13 | } 14 | 15 | bool fit(int now, int flag) { 16 | return (now | flag) == flag; 17 | } 18 | bool ok(int x) { 19 | // 行内自己是否相交 20 | return (x & (x / 2)) == 0; 21 | } 22 | int count(int now) { 23 | int s = 0; // 统计 now 的二进制形式中有多少个 1 24 | while (now) { 25 | s += (now & 1); // 判断 now 二进制的最后一位是否为 1,如果是则累加 26 | now >>= 1; // now 右移一位 27 | } 28 | return s; 29 | } 30 | 31 | int main() { 32 | int n, m; 33 | cin >> n >> m; 34 | // 初始化所有数组 35 | for (int i = 1; i <= n; ++i) { 36 | for (int j = 0; j < m; ++j) { 37 | int flag; 38 | cin >> flag; 39 | state[i] |= (1 << j) * flag; // 将 (i,j) 格子的状态放入 state[i] 中,state[i] 表示第 i 行的可选格子组成的集合 40 | } 41 | } 42 | for (int i = 1; i <= n; ++i) { 43 | for (int j = 0; j < (1 << m); ++j) { // 枚举当前行的状态 44 | if (!ok(j) || !fit(j, state[i])) { // 如果当前行状态不合法则不执行后面的枚举 45 | continue; 46 | } 47 | int cnt = count(j); // 统计当前行一共选了多少个格子 48 | for (int k = 0; k < (1 << m); ++k) { 49 | if (ok(k) && fit(k, state[i - 1]) && not_intersect(j, k)) { // 判断前一行是否合法和当前行和前一行的方案是否冲突 50 | dp[i][j] = max(dp[i][j], dp[i - 1][k] + cnt); // 更新当前行、当前状态的最优解 51 | } 52 | } 53 | } 54 | } 55 | int ans = 0; // 保存最终答案 56 | for (int i = 0; i < (1 << m); ++i) { 57 | ans = max(ans, dp[n][i]); // 枚举所有状态,更新最大值 58 | } 59 | cout << ans << endl; 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /3_dp/content.md: -------------------------------------------------------------------------------- 1 | # 动态规划基础模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|01 背包模板|1|| 7 | |跟随|最长上升子序列模板|1|| 8 | |跟随|最长公共子序列模板|1|| 9 | |跟随|状态压缩动态规划模板|1|| 10 | 11 | 动态规划。 12 | -------------------------------------------------------------------------------- /4_math/1_gcd.md: -------------------------------------------------------------------------------- 1 | # gcd 的使用 2 | - gcd 3 | 4 | ### 文件名 5 | gcd.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 这一节我们学习如何求两个整数的最大公因数。 26 | 最大公因数,也称最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。 27 | 先定义两个整数`n`和`m`并从键盘输入。在`main`函数里写下 28 | ```c++ 29 | int n, m; 30 | cin >> n >> m; 31 | ``` 32 | 33 | #### 代码 34 | ```c++ 35 | #include 36 | using namespace std; 37 | 38 | int main() { 39 | int n, m; 40 | cin >> n >> m; 41 | 42 | return 0; 43 | } 44 | ``` 45 | 46 | --- 47 | ### 第二步 48 | #### 讲解 49 | 首先我们定义一个`gcd`函数,这个函数用来用来求传入的两个参数的最大公约数。 50 | 51 | 在`main`函数上方写下 52 | ```c++ 53 | int gcd(int a, int b) { 54 | 55 | } 56 | ``` 57 | 58 | #### 提示 59 | ```c++ 60 | #include 61 | using namespace std; 62 | int gcd(int a, int b) { 63 | 64 | } 65 | int main() { 66 | int n, m; 67 | cin >> n >> m; 68 | 69 | return 0; 70 | } 71 | ``` 72 | 73 | #### 代码 74 | ```c++ 75 | #include 76 | using namespace std; 77 | int gcd(int a, int b) { 78 | 79 | } 80 | int main() { 81 | int n, m; 82 | cin >> n >> m; 83 | 84 | return 0; 85 | } 86 | ``` 87 | 88 | --- 89 | ### 第三步 90 | #### 讲解 91 | 接下来我们要实现`gcd`的具体过程。 92 | 我们要用到辗转相除的方法来计算两个数的最大公约数。 93 | 过程如下: 94 | 1、a % b得余数c 95 | 2、若c = 0,则b即为两数的最大公约数 96 | 3、若c != 0,则a = b, b = c,再回去执行过程1 97 | 根据上面的思路我们可以实现代码如下 98 | ```c++ 99 | int gcd(int a, int b) { 100 | if (b == 0) { 101 | return a; 102 | } 103 | return gcd(b, a % b); 104 | } 105 | ``` 106 | 107 | #### 提示 108 | ```c++ 109 | #include 110 | using namespace std; 111 | int gcd(int a, int b) { 112 | if (b == 0) { 113 | return a; 114 | } 115 | return gcd(b, a % b); 116 | } 117 | int main() { 118 | int n, m; 119 | cin >> n >> m; 120 | 121 | return 0; 122 | } 123 | ``` 124 | 125 | 126 | #### 代码 127 | ```c++ 128 | #include 129 | using namespace std; 130 | int gcd(int a, int b) { 131 | if (b == 0) { 132 | return a; 133 | } 134 | return gcd(b, a % b); 135 | } 136 | int main() { 137 | int n, m; 138 | cin >> n >> m; 139 | 140 | return 0; 141 | } 142 | ``` 143 | 144 | 145 | --- 146 | ### 第四步 147 | #### 讲解 148 | 最后输出所求的`gcd`的值。在`main`里继续写下 149 | ```c++ 150 | cout << gcd(n, m) << endl; 151 | ``` 152 | 153 | #### 提示 154 | ```c++ 155 | #include 156 | using namespace std; 157 | int gcd(int a, int b) { 158 | if (b == 0) { 159 | return a; 160 | } 161 | return gcd(b, a % b); 162 | } 163 | int main() { 164 | int n, m; 165 | cin >> n >> m; 166 | cout << gcd(n, m) << endl; 167 | return 0; 168 | } 169 | ``` 170 | 171 | 172 | #### 代码 173 | ```c++ 174 | #include 175 | using namespace std; 176 | int gcd(int a, int b) { 177 | if (b == 0) { 178 | return a; 179 | } 180 | return gcd(b, a % b); 181 | } 182 | int main() { 183 | int n, m; 184 | cin >> n >> m; 185 | cout << gcd(n, m) << endl; 186 | return 0; 187 | } 188 | ``` 189 | 190 | 191 | 192 | --- 193 | ### 完成讲解 194 | 终于完成了,点击运行,输入下面的数据看看效果吧。 195 | ``` 196 | 8 12 197 | ``` 198 | -------------------------------------------------------------------------------- /4_math/2_prime.md: -------------------------------------------------------------------------------- 1 | # prime 的使用 2 | - prime 3 | 4 | ### 文件名 5 | prime.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 这一节我们学习如何素数打表,如求100以内的素数有哪些。 26 | 素数又称为质数,质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数。 27 | 先定义一个布尔类型的标记数组`is_prime`。在`main`函数上方写下 28 | ```c++ 29 | bool is_prime[100]; 30 | ``` 31 | 32 | #### 代码 33 | ```c++ 34 | #include 35 | using namespace std; 36 | bool is_prime[100]; 37 | int main() { 38 | 39 | return 0; 40 | } 41 | ``` 42 | 43 | --- 44 | ### 第二步 45 | #### 讲解 46 | 我们要用埃式筛法来快速筛选素数。 47 | 算法思路:我们从最小的素数2开始,把2的倍数的数排除,然后把3的倍数的数排除,因为4是2的倍数已经被排除了,所以再把5的倍数的数排除,一直这样把所有的素数的倍数排除,剩下的就只有素数了。 48 | 我们先初始化数组,在`main`函数里面写下 49 | ```c++ 50 | for (int i = 2; i < 100; ++i) { 51 | is_prime[i] = 1; 52 | } 53 | ``` 54 | 55 | #### 提示 56 | ```c++ 57 | #include 58 | using namespace std; 59 | bool is_prime[100]; 60 | int main() { 61 | for (int i = 2; i < 100; ++i) { 62 | is_prime[i] = 1; 63 | } 64 | 65 | return 0; 66 | } 67 | ``` 68 | 69 | #### 代码 70 | ```c++ 71 | #include 72 | using namespace std; 73 | bool is_prime[100]; 74 | int main() { 75 | for (int i = 2; i < 100; ++i) { 76 | is_prime[i] = 1; 77 | } 78 | 79 | return 0; 80 | } 81 | ``` 82 | 83 | --- 84 | ### 第三步 85 | #### 讲解 86 | 然后实现上面的埃式筛法。注意我们只需遍历到sqrt(100),因为后面是重复的。 87 | 根据上面的思路我们可以实现代码如下 88 | ```c++ 89 | for (int i = 2; i * i < 100; ++i) { 90 | if (is_prime[i]) { 91 | for (int j = i * i; j < 100; j +=i) { 92 | is_prime[j] = 0; 93 | } 94 | } 95 | } 96 | ``` 97 | 98 | #### 提示 99 | ```c++ 100 | #include 101 | using namespace std; 102 | bool is_prime[100]; 103 | int main() { 104 | for (int i = 2; i < 100; ++i) { 105 | is_prime[i] = 1; 106 | } 107 | for (int i = 2; i * i < 100; ++i) { 108 | if (is_prime[i]) { 109 | for (int j = i * i; j < 100; j +=i) { 110 | is_prime[j] = 0; 111 | } 112 | } 113 | } 114 | 115 | return 0; 116 | } 117 | ``` 118 | 119 | 120 | #### 代码 121 | ```c++ 122 | #include 123 | using namespace std; 124 | bool is_prime[100]; 125 | int main() { 126 | for (int i = 2; i < 100; ++i) { 127 | is_prime[i] = 1; 128 | } 129 | for (int i = 2; i * i < 100; ++i) { 130 | if (is_prime[i]) { 131 | for (int j = i * i; j < 100; j +=i) { 132 | is_prime[j] = 0; 133 | } 134 | } 135 | } 136 | 137 | return 0; 138 | } 139 | ``` 140 | 141 | 142 | --- 143 | ### 第四步 144 | #### 讲解 145 | 最后输出我们计算出来的100以内的所有素数的值。在`main`里继续写下 146 | ```c++ 147 | for (int i = 2; i < 100; ++i) { 148 | if (is_prime[i] == 1) { 149 | cout << i << " "; 150 | } 151 | } 152 | cout << endl; 153 | ``` 154 | 155 | #### 提示 156 | ```c++ 157 | #include 158 | using namespace std; 159 | bool is_prime[100]; 160 | int main() { 161 | for (int i = 2; i < 100; ++i) { 162 | is_prime[i] = 1; 163 | } 164 | for (int i = 2; i * i < 100; ++i) { 165 | if (is_prime[i]) { 166 | for (int j = i * i; j < 100; j +=i) { 167 | is_prime[j] = 0; 168 | } 169 | } 170 | } 171 | for (int i = 2; i < 100; ++i) { 172 | if (is_prime[i] == 1) { 173 | cout << i << " "; 174 | } 175 | } 176 | cout << endl; 177 | 178 | return 0; 179 | } 180 | ``` 181 | 182 | 183 | #### 代码 184 | ```c++ 185 | #include 186 | using namespace std; 187 | bool is_prime[100]; 188 | int main() { 189 | for (int i = 2; i < 100; ++i) { 190 | is_prime[i] = 1; 191 | } 192 | for (int i = 2; i * i < 100; ++i) { 193 | if (is_prime[i]) { 194 | for (int j = i * i; j < 100; j +=i) { 195 | is_prime[j] = 0; 196 | } 197 | } 198 | } 199 | for (int i = 2; i < 100; ++i) { 200 | if (is_prime[i] == 1) { 201 | cout << i << " "; 202 | } 203 | } 204 | cout << endl; 205 | 206 | return 0; 207 | } 208 | ``` 209 | 210 | 211 | 212 | --- 213 | ### 完成讲解 214 | 这一节已经完成了,赶紧运行看看效果。 215 | 216 | 聪明的你一定学会了怎么素数打表了。 217 | -------------------------------------------------------------------------------- /4_math/3_euler.md: -------------------------------------------------------------------------------- 1 | # euler 的使用 2 | - euler 3 | 4 | ### 文件名 5 | euler.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 这一节我们学习欧拉函数。 26 | 在数论,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(φ(1)=1) 27 | 互质是公约数只有1的两个整数,叫做互质整数。 28 | 先输入一个整数`n`,我们来求`n`的欧拉函数值。在`main`函数里面写下 29 | ```c++ 30 | int n; 31 | cin >> n; 32 | ``` 33 | 34 | #### 代码 35 | ```c++ 36 | #include 37 | using namespace std; 38 | int main() { 39 | int n; 40 | cin >> n; 41 | 42 | return 0; 43 | } 44 | ``` 45 | 46 | --- 47 | ### 第二步 48 | #### 讲解 49 | 先来看一个欧拉函数的性质 50 | 对于一个正整数N的素数幂分解N = P1^q1 * P2^q2 * ... * Pn^qn。 51 | φ(N) = N * (1 - 1/P1) * (1 - 1/P2) * ... * (1 - 1/Pn) 52 | 于是我们可以在O(sqrt(n))的时间内求出一个数的欧拉函数值 53 | 我们先初始化数组,在`main`函数里面写下 54 | ```c++ 55 | int res = n; 56 | for (int i = 2; i * i <= n; ++i) { 57 | if (n % i == 0) { 58 | res = res / i * (i - 1); //先进行除法是为了防止中间数据溢出 59 | while (n % i == 0) { 60 | n /= i; 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | #### 提示 67 | ```c++ 68 | #include 69 | using namespace std; 70 | int main() { 71 | int n; 72 | cin >> n; 73 | int res = n; 74 | for (int i = 2; i * i <= n; ++i) { 75 | if (n % i == 0) { 76 | res = res / i * (i - 1); //先进行除法是为了防止中间数据溢出 77 | while (n % i == 0) { 78 | n /= i; 79 | } 80 | } 81 | } 82 | 83 | return 0; 84 | } 85 | ``` 86 | 87 | #### 代码 88 | ```c++ 89 | #include 90 | using namespace std; 91 | int main() { 92 | int n; 93 | cin >> n; 94 | int res = n; 95 | for (int i = 2; i * i <= n; ++i) { 96 | if (n % i == 0) { 97 | res = res / i * (i - 1); //先进行除法是为了防止中间数据溢出 98 | while (n % i == 0) { 99 | n /= i; 100 | } 101 | } 102 | } 103 | 104 | return 0; 105 | } 106 | ``` 107 | 108 | --- 109 | ### 第三步 110 | #### 讲解 111 | 可以发现上面的遍历是经过优化的,只遍历到sqrt(n),但是有可能还剩一个较大的质数因子。 112 | 所以还需加上一下的代码 113 | ```c++ 114 | if (n > 1) { 115 | res = res / n * (n - 1); 116 | } 117 | ``` 118 | 119 | #### 提示 120 | ```c++ 121 | #include 122 | using namespace std; 123 | int main() { 124 | int n; 125 | cin >> n; 126 | int res = n; 127 | for (int i = 2; i * i <= n; ++i) { 128 | if (n % i == 0) { 129 | res = res / i * (i - 1); //先进行除法是为了防止中间数据溢出 130 | while (n % i == 0) { 131 | n /= i; 132 | } 133 | } 134 | } 135 | if (n > 1) { 136 | res = res / n * (n - 1); 137 | } 138 | 139 | return 0; 140 | } 141 | ``` 142 | 143 | 144 | #### 代码 145 | ```c++ 146 | #include 147 | using namespace std; 148 | int main() { 149 | int n; 150 | cin >> n; 151 | int res = n; 152 | for (int i = 2; i * i <= n; ++i) { 153 | if (n % i == 0) { 154 | res = res / i * (i - 1); //先进行除法是为了防止中间数据溢出 155 | while (n % i == 0) { 156 | n /= i; 157 | } 158 | } 159 | } 160 | if (n > 1) { 161 | res = res / n * (n - 1); 162 | } 163 | 164 | return 0; 165 | } 166 | ``` 167 | 168 | 169 | --- 170 | ### 第四步 171 | #### 讲解 172 | 最后输出我们计算出来的`n`的欧拉函数的值。在`main`里继续写下 173 | ```c++ 174 | cout << res << endl; 175 | ``` 176 | 177 | #### 提示 178 | ```c++ 179 | #include 180 | using namespace std; 181 | int main() { 182 | int n; 183 | cin >> n; 184 | int res = n; 185 | for (int i = 2; i * i <= n; ++i) { 186 | if (n % i == 0) { 187 | res = res / i * (i - 1); //先进行除法是为了防止中间数据溢出 188 | while (n % i == 0) { 189 | n /= i; 190 | } 191 | } 192 | } 193 | if (n > 1) { 194 | res = res / n * (n - 1); 195 | } 196 | cout << res << endl; 197 | return 0; 198 | } 199 | ``` 200 | 201 | 202 | #### 代码 203 | ```c++ 204 | #include 205 | using namespace std; 206 | int main() { 207 | int n; 208 | cin >> n; 209 | int res = n; 210 | for (int i = 2; i * i <= n; ++i) { 211 | if (n % i == 0) { 212 | res = res / i * (i - 1); //先进行除法是为了防止中间数据溢出 213 | while (n % i == 0) { 214 | n /= i; 215 | } 216 | } 217 | } 218 | if (n > 1) { 219 | res = res / n * (n - 1); 220 | } 221 | cout << res << endl; 222 | return 0; 223 | } 224 | ``` 225 | 226 | 227 | 228 | --- 229 | ### 完成讲解 230 | 终于完成了,点击运行,输入下面的数据看看效果吧。 231 | ``` 232 | 18 233 | ``` 234 | 聪明的你一定学会了如何求一个数的欧拉函数值了吧。 235 | -------------------------------------------------------------------------------- /4_math/4_exgcd.md: -------------------------------------------------------------------------------- 1 | # exgcd 的使用 2 | - exgcd 3 | 4 | ### 文件名 5 | exgcd.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 这一节我们学习`exgcd`(扩展GCD)算法。 26 | 首先来看一个结论:对于不完全为`0`的非负整数`a`,`b`,`gcd(a,b)`表示 `a`,`b` 的最大公约数,必然存在整 27 | 数对 `x`,`y` ,使得 `gcd(a,b)= ax + by`。 28 | `exgcd`就是在已知`a`,`b`的情况下求解`x`和`y`。 29 | 先输入`a`和`b`两个整数。在`main`函数里面写下 30 | ```c++ 31 | int a, b, x, y; 32 | cin >> a >> b; 33 | ``` 34 | 35 | #### 代码 36 | ```c++ 37 | #include 38 | using namespace std; 39 | int main() { 40 | int a, b, x, y; 41 | cin >> a >> b; 42 | 43 | return 0; 44 | } 45 | ``` 46 | 47 | --- 48 | ### 第二步 49 | #### 讲解 50 | 然后将`exgcd`的函数框架写好。在`main`函数上面写下 51 | ```c++ 52 | int exgcd(int a, int b, int &x, int &y) { 53 | 54 | } 55 | ``` 56 | 57 | #### 提示 58 | ```c++ 59 | #include 60 | using namespace std; 61 | int exgcd(int a, int b, int &x, int &y) { 62 | 63 | } 64 | int main() { 65 | int a, b, x, y; 66 | cin >> a >> b; 67 | 68 | return 0; 69 | } 70 | ``` 71 | 72 | #### 代码 73 | ```c++ 74 | #include 75 | using namespace std; 76 | int exgcd(int a, int b, int &x, int &y) { 77 | 78 | } 79 | int main() { 80 | int a, b, x, y; 81 | cin >> a >> b; 82 | 83 | return 0; 84 | } 85 | ``` 86 | 87 | --- 88 | ### 第三步 89 | #### 讲解 90 | 接下来我们来简单整理一下思路。 91 | 1、当 b = 0,gcd(a,b)= a,此时 x = 1,y = 0。 92 | 2、对于方程ax + by = gcd(a, b);我们设解为x1, y1 93 | 我们令a = b, b = a % b; 94 | 得到方程bx + a % by = gcd(b, a % b); 95 | 由欧几里得算法可以得到gcd(a, b) = gcd(b, a % b); 96 | 代入可得:bx + a % b y = gcd(a, b) 97 | 设此方程解为x2, y2; 98 | 将模运算转换可得: a % b = a - (a / b) * b; 99 | 代入方程化解得: 100 | ay2 + b(x2 - (a / b) y2) = gcd(a, b); 101 | 与ax1 + by1 = gcd(a, b) 联立,我们很容易得: 102 | x1 = y2, y1 = x2 - (a / b)y2; 103 | 然后就可以递归求解。根据这个思路我们可以实现第一步代码如下 104 | ```c++ 105 | int exgcd(int a, int b, int &x, int &y) { 106 | if(b == 0) { 107 | x = 1; 108 | y = 0; 109 | return a; 110 | } 111 | } 112 | ``` 113 | 114 | #### 提示 115 | ```c++ 116 | #include 117 | using namespace std; 118 | int exgcd(int a, int b, int &x, int &y) { 119 | if(b == 0) { 120 | x = 1; 121 | y = 0; 122 | return a; 123 | } 124 | } 125 | int main() { 126 | int a, b, x, y; 127 | cin >> a >> b; 128 | 129 | return 0; 130 | } 131 | ``` 132 | 133 | 134 | #### 代码 135 | ```c++ 136 | #include 137 | using namespace std; 138 | int exgcd(int a, int b, int &x, int &y) { 139 | if(b == 0) { 140 | x = 1; 141 | y = 0; 142 | return a; 143 | } 144 | } 145 | int main() { 146 | int a, b, x, y; 147 | cin >> a >> b; 148 | 149 | return 0; 150 | } 151 | ``` 152 | 153 | 154 | --- 155 | ### 第四步 156 | #### 讲解 157 | 接着实现第二步的代码。在`exgcd`里继续写下 158 | ```c++ 159 | int exgcd(int a, int b, int &x, int &y) { 160 | if(b == 0) { 161 | x = 1; 162 | y = 0; 163 | return a; 164 | } 165 | int r = exgcd(b, a % b, x, y); 166 | int t = x; 167 | x = y; 168 | y = t - a / b * y; 169 | return r; 170 | } 171 | ``` 172 | 173 | #### 提示 174 | ```c++ 175 | #include 176 | using namespace std; 177 | int exgcd(int a, int b, int &x, int &y) { 178 | if(b == 0) { 179 | x = 1; 180 | y = 0; 181 | return a; 182 | } 183 | int r = exgcd(b, a % b, x, y); 184 | int t = x; 185 | x = y; 186 | y = t - a / b * y; 187 | return r; 188 | } 189 | int main() { 190 | int a, b, x, y; 191 | cin >> a >> b; 192 | 193 | return 0; 194 | } 195 | ``` 196 | 197 | 198 | #### 代码 199 | ```c++ 200 | #include 201 | using namespace std; 202 | int exgcd(int a, int b, int &x, int &y) { 203 | if(b == 0) { 204 | x = 1; 205 | y = 0; 206 | return a; 207 | } 208 | int r = exgcd(b, a % b, x, y); 209 | int t = x; 210 | x = y; 211 | y = t - a / b * y; 212 | return r; 213 | } 214 | int main() { 215 | int a, b, x, y; 216 | cin >> a >> b; 217 | 218 | return 0; 219 | } 220 | ``` 221 | 222 | 223 | 224 | --- 225 | ### 第五步 226 | #### 讲解 227 | 然后我们调用已经写好的`exgcd`函数。在`main`里继续写下 228 | ```c++ 229 | int d = exgcd(a, b, x, y); 230 | ``` 231 | 232 | #### 提示 233 | ```c++ 234 | #include 235 | using namespace std; 236 | int exgcd(int a, int b, int &x, int &y) { 237 | if(b == 0) { 238 | x = 1; 239 | y = 0; 240 | return a; 241 | } 242 | int r = exgcd(b, a % b, x, y); 243 | int t = x; 244 | x = y; 245 | y = t - a / b * y; 246 | return r; 247 | } 248 | int main() { 249 | int a, b, x, y; 250 | cin >> a >> b; 251 | int d = exgcd(a, b, x, y); 252 | 253 | return 0; 254 | } 255 | ``` 256 | 257 | 258 | #### 代码 259 | ```c++ 260 | #include 261 | using namespace std; 262 | int exgcd(int a, int b, int &x, int &y) { 263 | if(b == 0) { 264 | x = 1; 265 | y = 0; 266 | return a; 267 | } 268 | int r = exgcd(b, a % b, x, y); 269 | int t = x; 270 | x = y; 271 | y = t - a / b * y; 272 | return r; 273 | } 274 | int main() { 275 | int a, b, x, y; 276 | cin >> a >> b; 277 | int d = exgcd(a, b, x, y); 278 | 279 | return 0; 280 | } 281 | ``` 282 | 283 | 284 | 285 | --- 286 | ### 第六步 287 | #### 讲解 288 | 最后输出结果。在`main`里继续写下 289 | ```c++ 290 | cout << a << " * " << x << " + " << b << " * " << y << " = " << d << endl; 291 | ``` 292 | 293 | #### 提示 294 | ```c++ 295 | #include 296 | using namespace std; 297 | int exgcd(int a, int b, int &x, int &y) { 298 | if(b == 0) { 299 | x = 1; 300 | y = 0; 301 | return a; 302 | } 303 | int r = exgcd(b, a % b, x, y); 304 | int t = x; 305 | x = y; 306 | y = t - a / b * y; 307 | return r; 308 | } 309 | int main() { 310 | int a, b, x, y; 311 | cin >> a >> b; 312 | int d = exgcd(a, b, x, y); 313 | cout << a << " * " << x << " + " << b << " * " << y << " = " << d << endl; 314 | return 0; 315 | } 316 | ``` 317 | 318 | 319 | #### 代码 320 | ```c++ 321 | #include 322 | using namespace std; 323 | int exgcd(int a, int b, int &x, int &y) { 324 | if(b == 0) { 325 | x = 1; 326 | y = 0; 327 | return a; 328 | } 329 | int r = exgcd(b, a % b, x, y); 330 | int t = x; 331 | x = y; 332 | y = t - a / b * y; 333 | return r; 334 | } 335 | int main() { 336 | int a, b, x, y; 337 | cin >> a >> b; 338 | int d = exgcd(a, b, x, y); 339 | cout << a << " * " << x << " + " << b << " * " << y << " = " << d << endl; 340 | return 0; 341 | } 342 | ``` 343 | 344 | 345 | --- 346 | ### 完成讲解 347 | 终于完成了,点击运行,输入下面的数据看看效果吧。 348 | ``` 349 | 7 9 350 | ``` 351 | 提示:当gcd(a, b) != 1时,我们很容易发现方程是无解的。 352 | -------------------------------------------------------------------------------- /4_math/5_binary_pow.md: -------------------------------------------------------------------------------- 1 | # binary_pow 的使用 2 | - binary_pow 3 | 4 | ### 文件名 5 | binary_pow.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 这一节我们学习`binary_pow`(二分快速幂)算法。 26 | 我们要求a ^ b % mod的值,可以发现当b非常大的时候,我们的需要将a乘以b次,O(b)的时间复杂度是让人难以接受的。 27 | 思考一下: 28 | 对于任意一个正整数b是否可以用1,2,4,8,16,32,....,2^n这样的数来组成。 29 | 比如7 = 4 + 2 + 1 30 | 19 = 16 + 2 + 1 31 | 聪明的你是否发现了其中的规律。 32 | 先输入`a`和`b`和`mod`三个整数。在`main`函数里面写下 33 | ```c++ 34 | int a, b, mod; 35 | cin >> a >> b >> mod; 36 | ``` 37 | 38 | #### 代码 39 | ```c++ 40 | #include 41 | using namespace std; 42 | int main() { 43 | int a, b, mod; 44 | cin >> a >> b >> mod; 45 | 46 | return 0; 47 | } 48 | ``` 49 | 50 | --- 51 | ### 第二步 52 | #### 讲解 53 | 根据刚才我们发现的规律,任何一个`b`都可以由2^i次幂的数构成,而且这些数不会有重复的。 54 | 于是我们可以用二分的思想来快速将`b`的值分解。 55 | 我们先将`Pow_mod`函数框架写好。在`main`函数上面写下 56 | ```c++ 57 | int Pow_mod(int a, int b, int mod){ 58 | 59 | } 60 | ``` 61 | 62 | #### 提示 63 | ```c++ 64 | #include 65 | using namespace std; 66 | int Pow_mod(int a, int b, int mod){ 67 | 68 | } 69 | int main() { 70 | int a, b, mod; 71 | cin >> a >> b >> mod; 72 | 73 | return 0; 74 | } 75 | ``` 76 | 77 | #### 代码 78 | ```c++ 79 | #include 80 | using namespace std; 81 | int Pow_mod(int a, int b, int mod){ 82 | 83 | } 84 | int main() { 85 | int a, b, mod; 86 | cin >> a >> b >> mod; 87 | 88 | return 0; 89 | } 90 | ``` 91 | 92 | --- 93 | ### 第三步 94 | #### 讲解 95 | 上面我们可以二分的方法快速将`b`分解成2^i次幂组合成的数。 96 | 例如a^7 = a^4 * a^2 * a^1 97 | a^19 = a^16 * a^2 * a^1 98 | 我们将`b`转化为二进制之后可以发现7是111,19是10011。 99 | 且a^2 = a^1 * a^1 100 | a^4 = a^2 * a^2 101 | 于是我们要快速计算一个数a^b,只需要看b的二进制位是0还是1。 102 | 根据这个思路我们可以实现代码如下 103 | ```c++ 104 | int Pow_mod(int a, int b, int mod){ 105 | int res = 1, temp = a; 106 | for (; b; b /= 2) { 107 | if (b & 1) { 108 | res = res * temp % mod; 109 | } 110 | temp = temp * temp % mod; 111 | } 112 | return res; 113 | } 114 | ``` 115 | 116 | #### 提示 117 | ```c++ 118 | #include 119 | using namespace std; 120 | int Pow_mod(int a, int b, int mod){ 121 | int res = 1, temp = a; 122 | for (; b; b /= 2) { 123 | if (b & 1) { 124 | res = res * temp % mod; 125 | } 126 | temp = temp * temp % mod; 127 | } 128 | return res; 129 | } 130 | int main() { 131 | int a, b, mod; 132 | cin >> a >> b >> mod; 133 | 134 | return 0; 135 | } 136 | ``` 137 | 138 | 139 | #### 代码 140 | ```c++ 141 | #include 142 | using namespace std; 143 | int Pow_mod(int a, int b, int mod){ 144 | int res = 1, temp = a; 145 | for (; b; b /= 2) { 146 | if (b & 1) { 147 | res = res * temp % mod; 148 | } 149 | temp = temp * temp % mod; 150 | } 151 | return res; 152 | } 153 | int main() { 154 | int a, b, mod; 155 | cin >> a >> b >> mod; 156 | 157 | return 0; 158 | } 159 | ``` 160 | 161 | 162 | --- 163 | ### 第四步 164 | #### 讲解 165 | 最后输出我们所求的结果。在`main`里继续写下 166 | ```c++ 167 | cout << Pow_mod(a, b, mod) << endl; 168 | ``` 169 | 170 | #### 提示 171 | ```c++ 172 | #include 173 | using namespace std; 174 | int Pow_mod(int a, int b, int mod){ 175 | int res = 1, temp = a; 176 | for (; b; b /= 2) { 177 | if (b & 1) { 178 | res = res * temp % mod; 179 | } 180 | temp = temp * temp % mod; 181 | } 182 | return res; 183 | } 184 | int main() { 185 | int a, b, mod; 186 | cin >> a >> b >> mod; 187 | cout << Pow_mod(a, b, mod) << endl; 188 | return 0; 189 | } 190 | ``` 191 | 192 | 193 | #### 代码 194 | ```c++ 195 | #include 196 | using namespace std; 197 | int Pow_mod(int a, int b, int mod){ 198 | int res = 1, temp = a; 199 | for (; b; b /= 2) { 200 | if (b & 1) { 201 | res = res * temp % mod; 202 | } 203 | temp = temp * temp % mod; 204 | } 205 | return res; 206 | } 207 | int main() { 208 | int a, b, mod; 209 | cin >> a >> b >> mod; 210 | cout << Pow_mod(a, b, mod) << endl; 211 | return 0; 212 | } 213 | ``` 214 | 215 | --- 216 | ### 完成讲解 217 | 终于完成了,点击运行,输入下面的数据看看效果吧。 218 | ``` 219 | 100 100 999 220 | ``` 221 | -------------------------------------------------------------------------------- /4_math/code/1_gcd.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | int gcd(int a, int b) { 4 | if (b == 0) { 5 | return a; 6 | } 7 | return gcd(b, a % b); 8 | } 9 | int main() { 10 | int n, m; 11 | cin >> n >> m; 12 | cout << gcd(n, m) << endl; 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /4_math/code/2_prime.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | bool is_prime[100]; 4 | int main() { 5 | for (int i = 2; i < 100; ++i) { 6 | is_prime[i] = 1; 7 | } 8 | for (int i = 2; i * i < 100; ++i) { 9 | if (is_prime[i]) { 10 | for (int j = i * i; j < 100; j +=i) { 11 | is_prime[j] = 0; 12 | } 13 | } 14 | } 15 | for (int i = 2; i < 100; ++i) { 16 | if (is_prime[i] == 1) { 17 | cout << i << " "; 18 | } 19 | } 20 | cout << endl; 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /4_math/code/3_euler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int main() { 6 | int n; 7 | cin >> n; 8 | int res = n; 9 | for (int i = 2; i * i <= n; ++i) { 10 | if (n % i == 0) { 11 | res = res / i * (i - 1); 12 | while (n % i == 0) { 13 | n /= i; 14 | } 15 | } 16 | } 17 | if (n > 1) { 18 | res = res / n * (n - 1); 19 | } 20 | cout << res << endl; 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /4_math/code/4_exgcd.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | // ax + by = gcd(a, b) 4 | int exgcd(int a, int b, int &x, int &y) { 5 | if(b == 0) { 6 | x = 1; 7 | y = 0; 8 | return a; 9 | } 10 | int r = exgcd(b, a % b, x, y); 11 | int t = x; 12 | x = y; 13 | y = t - a / b * y; 14 | return r; 15 | } 16 | 17 | int main() { 18 | int a, b, x, y; 19 | cin >> a >> b; 20 | int d = exgcd(a, b, x, y); 21 | cout << a << " * " << x << " + " << b << " * " << y << " = " << d << endl; 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /4_math/code/5_binary_pow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | int Pow_mod(int a, int b, int mod){ 5 | int res = 1, temp = a; 6 | for (; b; b /= 2) { 7 | if (b & 1) { 8 | res = res * temp % mod; 9 | } 10 | temp = temp * temp % mod; 11 | } 12 | return res; 13 | } 14 | 15 | int main() { 16 | int a, b, mod; 17 | cin >> a >> b >> mod; 18 | cout << Pow_mod(a, b, mod) << endl; 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /4_math/code/6_matrix_pow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | int n; 4 | struct matrix { 5 | int a[100][100]; 6 | }; 7 | matrix matrix_mul(matrix A, matrix B, int mod) { 8 | matrix C; 9 | for (int i = 0; i < n; ++i) { 10 | for (int j = 0; j < n; ++j) { 11 | C.a[i][j] = 0; 12 | for (int k = 0; k < n; ++k) { 13 | C.a[i][j] += A.a[i][k] * B.a[k][j] % mod; 14 | C.a[i][j] %= mod; 15 | } 16 | } 17 | } 18 | return C; 19 | } 20 | matrix unit() { 21 | matrix res; 22 | for (int i = 0; i < n; ++i) { 23 | for (int j = 0; j < n; ++j) { 24 | if (i == j) { 25 | res.a[i][j] = 1; 26 | } else { 27 | res.a[i][j] = 0; 28 | } 29 | } 30 | } 31 | return res; 32 | } 33 | matrix matrix_pow(matrix A, int y, int mod) { 34 | matrix res = unit(), temp = A; 35 | for (; y; y /= 2) { 36 | if (y & 1) { 37 | res = matrix_mul(res, temp, mod); 38 | } 39 | temp = matrix_mul(temp, temp, mod); 40 | } 41 | return res; 42 | } 43 | 44 | int main() { 45 | matrix A; 46 | n = 2; 47 | for (int i = 0; i < n; ++i) { 48 | for (int j = 0; j < n; ++j) { 49 | cin >> A.a[i][j]; 50 | } 51 | } 52 | int y, mod; 53 | cin >> y >> mod; 54 | matrix res = matrix_pow(A, y, mod); 55 | for (int i = 0; i < n; ++i) { 56 | for (int j = 0; j < n; ++j) { 57 | cout << res.a[i][j] << " "; 58 | } 59 | cout << endl; 60 | } 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /4_math/content.md: -------------------------------------------------------------------------------- 1 | # 基础数学 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|gcd 模板|1|| 7 | |跟随|质数筛选模板|1|| 8 | |跟随|欧拉函数模板|1|| 9 | |跟随|扩展欧几里得模板|1|| 10 | |跟随|二分快速幂模板|1|| 11 | |跟随|矩阵二分快速幂模板|1|| 12 | 13 | 基础数学。 14 | -------------------------------------------------------------------------------- /5_tree_grpah/1_adjacency_list.md: -------------------------------------------------------------------------------- 1 | # adjacency_list 的使用 2 | - adjacency_list 3 | 4 | ### 文件名 5 | adjacency_list.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | int main() { 18 | 19 | return 0; 20 | } 21 | ``` 22 | 23 | --- 24 | ### 第一步 25 | #### 讲解 26 | 这一节我们学习图的邻接表存储方法。 27 | 图的邻接表存储方法是一种顺序分配与链式分配相结合的存储方法。 28 | 在表示含n个顶点的图的邻接表中,为每个顶点建立一个单链表,第i个单链表中的节点表示依附于顶点i的边(对有向图是以顶点i为尾的边)。每个单链表上附设一个表头节点,将所有表头节点构成一个表头节点数组。 29 | 我们先定义好邻接表的结构。在`main`函数上面写下 30 | ```c++ 31 | const int MAX_N = 100; 32 | const int MAX_M = 10000; 33 | struct edge { 34 | int v, next; 35 | int len; 36 | } E[MAX_M]; 37 | int p[MAX_N], eid; 38 | ``` 39 | 40 | #### 代码 41 | ```c++ 42 | #include 43 | #include 44 | using namespace std; 45 | 46 | const int MAX_N = 100; 47 | const int MAX_M = 10000; 48 | struct edge { 49 | int v, next; 50 | int len; 51 | } E[MAX_M]; 52 | int p[MAX_N], eid; 53 | 54 | int main() { 55 | 56 | return 0; 57 | } 58 | ``` 59 | 60 | --- 61 | ### 第二步 62 | #### 讲解 63 | 接下来我们初始化邻接表。 64 | 定义一个`init`函数。 65 | 在`main`函数上面写下 66 | ```c++ 67 | void init() { 68 | memset(p, -1, sizeof(p)); 69 | eid = 0; 70 | } 71 | ``` 72 | 73 | #### 提示 74 | ```c++ 75 | #include 76 | #include 77 | using namespace std; 78 | 79 | const int MAX_N = 100; 80 | const int MAX_M = 10000; 81 | struct edge { 82 | int v, next; 83 | int len; 84 | } E[MAX_M]; 85 | int p[MAX_N], eid; 86 | void init() { 87 | memset(p, -1, sizeof(p)); 88 | eid = 0; 89 | } 90 | 91 | int main() { 92 | 93 | return 0; 94 | } 95 | ``` 96 | 97 | #### 代码 98 | ```c++ 99 | #include 100 | #include 101 | using namespace std; 102 | 103 | const int MAX_N = 100; 104 | const int MAX_M = 10000; 105 | struct edge { 106 | int v, next; 107 | int len; 108 | } E[MAX_M]; 109 | int p[MAX_N], eid; 110 | void init() { 111 | memset(p, -1, sizeof(p)); 112 | eid = 0; 113 | } 114 | 115 | int main() { 116 | 117 | return 0; 118 | } 119 | ``` 120 | 121 | --- 122 | ### 第三步 123 | #### 讲解 124 | 然后在`main`函数中调用初始化邻接表的函数。在`main`函数里继续写下 125 | ```c++ 126 | init(); 127 | ``` 128 | 129 | #### 提示 130 | ```c++ 131 | #include 132 | #include 133 | using namespace std; 134 | 135 | const int MAX_N = 100; 136 | const int MAX_M = 10000; 137 | struct edge { 138 | int v, next; 139 | int len; 140 | } E[MAX_M]; 141 | int p[MAX_N], eid; 142 | void init() { 143 | memset(p, -1, sizeof(p)); 144 | eid = 0; 145 | } 146 | 147 | int main() { 148 | init(); 149 | 150 | return 0; 151 | } 152 | ``` 153 | 154 | 155 | #### 代码 156 | ```c++ 157 | #include 158 | #include 159 | using namespace std; 160 | 161 | const int MAX_N = 100; 162 | const int MAX_M = 10000; 163 | struct edge { 164 | int v, next; 165 | int len; 166 | } E[MAX_M]; 167 | int p[MAX_N], eid; 168 | void init() { 169 | memset(p, -1, sizeof(p)); 170 | eid = 0; 171 | } 172 | 173 | int main() { 174 | init(); 175 | 176 | return 0; 177 | } 178 | ``` 179 | 180 | 181 | --- 182 | ### 第四步 183 | #### 讲解 184 | 接下来实现邻接表的插入操作`insert`函数。 185 | 注意:`p`数组是表头节点,然后`E`中存储的边采用的头插法。 186 | 可以理解为用数组实现哈希冲突中的拉链法的感觉。 187 | 在`main`函数上方写下 188 | ```c++ 189 | void insert(int u, int v, int len) { 190 | E[eid].v = v; 191 | E[eid].len = len; 192 | E[eid].next = p[u]; 193 | p[u] = eid++; 194 | } 195 | ``` 196 | 197 | #### 提示 198 | ```c++ 199 | #include 200 | #include 201 | using namespace std; 202 | 203 | const int MAX_N = 100; 204 | const int MAX_M = 10000; 205 | struct edge { 206 | int v, next; 207 | int len; 208 | } E[MAX_M]; 209 | int p[MAX_N], eid; 210 | void init() { 211 | memset(p, -1, sizeof(p)); 212 | eid = 0; 213 | } 214 | void insert(int u, int v, int len) { 215 | E[eid].v = v; 216 | E[eid].len = len; 217 | E[eid].next = p[u]; 218 | p[u] = eid++; 219 | } 220 | 221 | int main() { 222 | init(); 223 | 224 | return 0; 225 | } 226 | ``` 227 | 228 | 229 | #### 代码 230 | ```c++ 231 | #include 232 | #include 233 | using namespace std; 234 | 235 | const int MAX_N = 100; 236 | const int MAX_M = 10000; 237 | struct edge { 238 | int v, next; 239 | int len; 240 | } E[MAX_M]; 241 | int p[MAX_N], eid; 242 | void init() { 243 | memset(p, -1, sizeof(p)); 244 | eid = 0; 245 | } 246 | void insert(int u, int v, int len) { 247 | E[eid].v = v; 248 | E[eid].len = len; 249 | E[eid].next = p[u]; 250 | p[u] = eid++; 251 | } 252 | 253 | int main() { 254 | init(); 255 | 256 | return 0; 257 | } 258 | ``` 259 | 260 | 261 | --- 262 | ### 第五步 263 | #### 讲解 264 | 接下来就可以插入图的边了。在`main`函数里面写下 265 | ```c++ 266 | insert(1, 2, 10); 267 | insert(1, 3, 5); 268 | insert(2, 3, 1); 269 | insert(3, 5, 10); 270 | insert(3, 4, 6); 271 | insert(5, 4, 11); 272 | ``` 273 | 274 | #### 提示 275 | ```c++ 276 | #include 277 | #include 278 | using namespace std; 279 | 280 | const int MAX_N = 100; 281 | const int MAX_M = 10000; 282 | struct edge { 283 | int v, next; 284 | int len; 285 | } E[MAX_M]; 286 | int p[MAX_N], eid; 287 | void init() { 288 | memset(p, -1, sizeof(p)); 289 | eid = 0; 290 | } 291 | void insert(int u, int v, int len) { 292 | E[eid].v = v; 293 | E[eid].len = len; 294 | E[eid].next = p[u]; 295 | p[u] = eid++; 296 | } 297 | 298 | int main() { 299 | init(); 300 | insert(1, 2, 10); 301 | insert(1, 3, 5); 302 | insert(2, 3, 1); 303 | insert(3, 5, 10); 304 | insert(3, 4, 6); 305 | insert(5, 4, 11); 306 | 307 | return 0; 308 | } 309 | ``` 310 | 311 | 312 | #### 代码 313 | ```c++ 314 | #include 315 | #include 316 | using namespace std; 317 | 318 | const int MAX_N = 100; 319 | const int MAX_M = 10000; 320 | struct edge { 321 | int v, next; 322 | int len; 323 | } E[MAX_M]; 324 | int p[MAX_N], eid; 325 | void init() { 326 | memset(p, -1, sizeof(p)); 327 | eid = 0; 328 | } 329 | void insert(int u, int v, int len) { 330 | E[eid].v = v; 331 | E[eid].len = len; 332 | E[eid].next = p[u]; 333 | p[u] = eid++; 334 | } 335 | 336 | int main() { 337 | init(); 338 | insert(1, 2, 10); 339 | insert(1, 3, 5); 340 | insert(2, 3, 1); 341 | insert(3, 5, 10); 342 | insert(3, 4, 6); 343 | insert(5, 4, 11); 344 | 345 | return 0; 346 | } 347 | ``` 348 | 349 | 350 | --- 351 | ### 第六步 352 | #### 讲解 353 | 接下来我们要输出刚才使用邻接表方法插入的边。 354 | 在`main`函数里继续写下 355 | ```c++ 356 | for (int u = 1; u <= 5; ++u) { 357 | for (int i = p[u]; i + 1; i = E[i].next) { 358 | cout << "(" << u << ", " << E[i].v << ")" << endl; 359 | } 360 | } 361 | ``` 362 | 363 | #### 提示 364 | ```c++ 365 | #include 366 | #include 367 | using namespace std; 368 | 369 | const int MAX_N = 100; 370 | const int MAX_M = 10000; 371 | struct edge { 372 | int v, next; 373 | int len; 374 | } E[MAX_M]; 375 | int p[MAX_N], eid; 376 | void init() { 377 | memset(p, -1, sizeof(p)); 378 | eid = 0; 379 | } 380 | void insert(int u, int v, int len) { 381 | E[eid].v = v; 382 | E[eid].len = len; 383 | E[eid].next = p[u]; 384 | p[u] = eid++; 385 | } 386 | 387 | int main() { 388 | init(); 389 | insert(1, 2, 10); 390 | insert(1, 3, 5); 391 | insert(2, 3, 1); 392 | insert(3, 5, 10); 393 | insert(3, 4, 6); 394 | insert(5, 4, 11); 395 | for (int u = 1; u <= 5; ++u) { 396 | for (int i = p[u]; i + 1; i = E[i].next) { 397 | cout << "(" << u << ", " << E[i].v << ")" << endl; 398 | } 399 | } 400 | return 0; 401 | } 402 | ``` 403 | 404 | 405 | #### 代码 406 | ```c++ 407 | #include 408 | #include 409 | using namespace std; 410 | 411 | const int MAX_N = 100; 412 | const int MAX_M = 10000; 413 | struct edge { 414 | int v, next; 415 | int len; 416 | } E[MAX_M]; 417 | int p[MAX_N], eid; 418 | void init() { 419 | memset(p, -1, sizeof(p)); 420 | eid = 0; 421 | } 422 | void insert(int u, int v, int len) { 423 | E[eid].v = v; 424 | E[eid].len = len; 425 | E[eid].next = p[u]; 426 | p[u] = eid++; 427 | } 428 | 429 | int main() { 430 | init(); 431 | insert(1, 2, 10); 432 | insert(1, 3, 5); 433 | insert(2, 3, 1); 434 | insert(3, 5, 10); 435 | insert(3, 4, 6); 436 | insert(5, 4, 11); 437 | for (int u = 1; u <= 5; ++u) { 438 | for (int i = p[u]; i + 1; i = E[i].next) { 439 | cout << "(" << u << ", " << E[i].v << ")" << endl; 440 | } 441 | } 442 | return 0; 443 | } 444 | ``` 445 | 446 | 447 | --- 448 | ### 完成讲解 449 | 终于完成了,点击运行,看看效果吧。 450 | 451 | 聪明的你一定学会了如何使用`邻接表`了。 452 | -------------------------------------------------------------------------------- /5_tree_grpah/2_graph_dfs.md: -------------------------------------------------------------------------------- 1 | # graph_dfs 的使用 2 | - graph_dfs 3 | 4 | ### 文件名 5 | graph_dfs.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | int main() { 18 | 19 | return 0; 20 | } 21 | ``` 22 | 23 | --- 24 | ### 第一步 25 | #### 讲解 26 | 这一节我们学习图的深度优先遍历(DFS)算法。 27 | 深度优先遍历的过程是:从图中某个初始顶点v出发,首先访问初始顶点v,然后选择一个与顶点v相邻且没被访问过的顶点w为初始顶点,再从w出发进行深度优先遍历,直到图中与当前顶点v邻接的所有顶点都被访问过为止。 28 | 显然,这个遍历过程是个递归过程。 29 | 我们先定义好邻接表的结构。在`main`函数上面写下 30 | ```c++ 31 | const int MAX_N = 100; 32 | const int MAX_M = 10000; 33 | struct edge { 34 | int v, next; 35 | int len; 36 | } E[MAX_M]; 37 | int p[MAX_N], eid; 38 | ``` 39 | 40 | #### 代码 41 | ```c++ 42 | #include 43 | #include 44 | using namespace std; 45 | 46 | const int MAX_N = 100; 47 | const int MAX_M = 10000; 48 | struct edge { 49 | int v, next; 50 | int len; 51 | } E[MAX_M]; 52 | int p[MAX_N], eid; 53 | 54 | int main() { 55 | 56 | return 0; 57 | } 58 | ``` 59 | 60 | --- 61 | ### 第二步 62 | #### 讲解 63 | 接下来我们初始化邻接表。 64 | 定义一个`init`函数。 65 | 在`main`函数上面写下 66 | ```c++ 67 | void init() { 68 | memset(p, -1, sizeof(p)); 69 | eid = 0; 70 | } 71 | ``` 72 | 73 | #### 提示 74 | ```c++ 75 | #include 76 | #include 77 | using namespace std; 78 | 79 | const int MAX_N = 100; 80 | const int MAX_M = 10000; 81 | struct edge { 82 | int v, next; 83 | int len; 84 | } E[MAX_M]; 85 | int p[MAX_N], eid; 86 | void init() { 87 | memset(p, -1, sizeof(p)); 88 | eid = 0; 89 | } 90 | 91 | int main() { 92 | 93 | return 0; 94 | } 95 | ``` 96 | 97 | #### 代码 98 | ```c++ 99 | #include 100 | #include 101 | using namespace std; 102 | 103 | const int MAX_N = 100; 104 | const int MAX_M = 10000; 105 | struct edge { 106 | int v, next; 107 | int len; 108 | } E[MAX_M]; 109 | int p[MAX_N], eid; 110 | void init() { 111 | memset(p, -1, sizeof(p)); 112 | eid = 0; 113 | } 114 | 115 | int main() { 116 | 117 | return 0; 118 | } 119 | ``` 120 | 121 | --- 122 | ### 第三步 123 | #### 讲解 124 | 然后在`main`函数中调用初始化邻接表的函数。在`main`函数里继续写下 125 | ```c++ 126 | init(); 127 | ``` 128 | 129 | #### 提示 130 | ```c++ 131 | #include 132 | #include 133 | using namespace std; 134 | 135 | const int MAX_N = 100; 136 | const int MAX_M = 10000; 137 | struct edge { 138 | int v, next; 139 | int len; 140 | } E[MAX_M]; 141 | int p[MAX_N], eid; 142 | void init() { 143 | memset(p, -1, sizeof(p)); 144 | eid = 0; 145 | } 146 | 147 | int main() { 148 | init(); 149 | 150 | return 0; 151 | } 152 | ``` 153 | 154 | 155 | #### 代码 156 | ```c++ 157 | #include 158 | #include 159 | using namespace std; 160 | 161 | const int MAX_N = 100; 162 | const int MAX_M = 10000; 163 | struct edge { 164 | int v, next; 165 | int len; 166 | } E[MAX_M]; 167 | int p[MAX_N], eid; 168 | void init() { 169 | memset(p, -1, sizeof(p)); 170 | eid = 0; 171 | } 172 | 173 | int main() { 174 | init(); 175 | 176 | return 0; 177 | } 178 | ``` 179 | 180 | 181 | --- 182 | ### 第四步 183 | #### 讲解 184 | 接下来实现邻接表的插入操作`insert`函数。 185 | 在`main`函数上方写下 186 | ```c++ 187 | void insert(int u, int v, int len) { 188 | E[eid].v = v; 189 | E[eid].len = len; 190 | E[eid].next = p[u]; 191 | p[u] = eid++; 192 | } 193 | ``` 194 | 195 | #### 提示 196 | ```c++ 197 | #include 198 | #include 199 | using namespace std; 200 | 201 | const int MAX_N = 100; 202 | const int MAX_M = 10000; 203 | struct edge { 204 | int v, next; 205 | int len; 206 | } E[MAX_M]; 207 | int p[MAX_N], eid; 208 | void init() { 209 | memset(p, -1, sizeof(p)); 210 | eid = 0; 211 | } 212 | void insert(int u, int v, int len) { 213 | E[eid].v = v; 214 | E[eid].len = len; 215 | E[eid].next = p[u]; 216 | p[u] = eid++; 217 | } 218 | 219 | int main() { 220 | init(); 221 | 222 | return 0; 223 | } 224 | ``` 225 | 226 | 227 | #### 代码 228 | ```c++ 229 | #include 230 | #include 231 | using namespace std; 232 | 233 | const int MAX_N = 100; 234 | const int MAX_M = 10000; 235 | struct edge { 236 | int v, next; 237 | int len; 238 | } E[MAX_M]; 239 | int p[MAX_N], eid; 240 | void init() { 241 | memset(p, -1, sizeof(p)); 242 | eid = 0; 243 | } 244 | void insert(int u, int v, int len) { 245 | E[eid].v = v; 246 | E[eid].len = len; 247 | E[eid].next = p[u]; 248 | p[u] = eid++; 249 | } 250 | 251 | int main() { 252 | init(); 253 | 254 | return 0; 255 | } 256 | ``` 257 | 258 | 259 | --- 260 | ### 第五步 261 | #### 讲解 262 | 接下来就可以插入图的边了。在`main`函数里面写下 263 | ```c++ 264 | insert(1, 2, 10); 265 | insert(1, 3, 5); 266 | insert(2, 3, 1); 267 | insert(3, 5, 10); 268 | insert(3, 4, 6); 269 | insert(5, 4, 11); 270 | ``` 271 | 272 | #### 提示 273 | ```c++ 274 | #include 275 | #include 276 | using namespace std; 277 | 278 | const int MAX_N = 100; 279 | const int MAX_M = 10000; 280 | struct edge { 281 | int v, next; 282 | int len; 283 | } E[MAX_M]; 284 | int p[MAX_N], eid; 285 | void init() { 286 | memset(p, -1, sizeof(p)); 287 | eid = 0; 288 | } 289 | void insert(int u, int v, int len) { 290 | E[eid].v = v; 291 | E[eid].len = len; 292 | E[eid].next = p[u]; 293 | p[u] = eid++; 294 | } 295 | 296 | int main() { 297 | init(); 298 | insert(1, 2, 10); 299 | insert(1, 3, 5); 300 | insert(2, 3, 1); 301 | insert(3, 5, 10); 302 | insert(3, 4, 6); 303 | insert(5, 4, 11); 304 | 305 | return 0; 306 | } 307 | ``` 308 | 309 | 310 | #### 代码 311 | ```c++ 312 | #include 313 | #include 314 | using namespace std; 315 | 316 | const int MAX_N = 100; 317 | const int MAX_M = 10000; 318 | struct edge { 319 | int v, next; 320 | int len; 321 | } E[MAX_M]; 322 | int p[MAX_N], eid; 323 | void init() { 324 | memset(p, -1, sizeof(p)); 325 | eid = 0; 326 | } 327 | void insert(int u, int v, int len) { 328 | E[eid].v = v; 329 | E[eid].len = len; 330 | E[eid].next = p[u]; 331 | p[u] = eid++; 332 | } 333 | 334 | int main() { 335 | init(); 336 | insert(1, 2, 10); 337 | insert(1, 3, 5); 338 | insert(2, 3, 1); 339 | insert(3, 5, 10); 340 | insert(3, 4, 6); 341 | insert(5, 4, 11); 342 | 343 | return 0; 344 | } 345 | ``` 346 | 347 | 348 | --- 349 | ### 第六步 350 | #### 讲解 351 | 接下来我们要实现`dfs`函数。 352 | 在`main`函数上面写下 353 | ```c++ 354 | bool vst[MAX_N]; 355 | void dfs(int u) { 356 | cout << "visiting " << u << endl; 357 | vst[u] = true; 358 | for (int i = p[u]; i + 1; i= E[i].next) { 359 | if (!vst[E[i].v]) { 360 | dfs(E[i].v); 361 | } 362 | } 363 | } 364 | ``` 365 | 366 | #### 提示 367 | ```c++ 368 | #include 369 | #include 370 | using namespace std; 371 | 372 | const int MAX_N = 100; 373 | const int MAX_M = 10000; 374 | struct edge { 375 | int v, next; 376 | int len; 377 | } E[MAX_M]; 378 | int p[MAX_N], eid; 379 | void init() { 380 | memset(p, -1, sizeof(p)); 381 | eid = 0; 382 | } 383 | void insert(int u, int v, int len) { 384 | E[eid].v = v; 385 | E[eid].len = len; 386 | E[eid].next = p[u]; 387 | p[u] = eid++; 388 | } 389 | 390 | bool vst[MAX_N]; 391 | void dfs(int u) { 392 | cout << "visiting " << u << endl; 393 | vst[u] = true; 394 | for (int i = p[u]; i + 1; i= E[i].next) { 395 | if (!vst[E[i].v]) { 396 | dfs(E[i].v); 397 | } 398 | } 399 | } 400 | int main() { 401 | init(); 402 | insert(1, 2, 10); 403 | insert(1, 3, 5); 404 | insert(2, 3, 1); 405 | insert(3, 5, 10); 406 | insert(3, 4, 6); 407 | insert(5, 4, 11); 408 | 409 | return 0; 410 | } 411 | ``` 412 | 413 | 414 | #### 代码 415 | ```c++ 416 | #include 417 | #include 418 | using namespace std; 419 | 420 | const int MAX_N = 100; 421 | const int MAX_M = 10000; 422 | struct edge { 423 | int v, next; 424 | int len; 425 | } E[MAX_M]; 426 | int p[MAX_N], eid; 427 | void init() { 428 | memset(p, -1, sizeof(p)); 429 | eid = 0; 430 | } 431 | void insert(int u, int v, int len) { 432 | E[eid].v = v; 433 | E[eid].len = len; 434 | E[eid].next = p[u]; 435 | p[u] = eid++; 436 | } 437 | 438 | bool vst[MAX_N]; 439 | void dfs(int u) { 440 | cout << "visiting " << u << endl; 441 | vst[u] = true; 442 | for (int i = p[u]; i + 1; i= E[i].next) { 443 | if (!vst[E[i].v]) { 444 | dfs(E[i].v); 445 | } 446 | } 447 | } 448 | int main() { 449 | init(); 450 | insert(1, 2, 10); 451 | insert(1, 3, 5); 452 | insert(2, 3, 1); 453 | insert(3, 5, 10); 454 | insert(3, 4, 6); 455 | insert(5, 4, 11); 456 | 457 | return 0; 458 | } 459 | ``` 460 | 461 | 462 | --- 463 | ### 第七步 464 | #### 讲解 465 | 最后在`main`函数里初始化`vst`标记数组并以顶点`1`作为起点进行深度优先遍历。 466 | 在`main`函数里面写下 467 | ```c++ 468 | memset(vst, false, sizeof(vst)); 469 | dfs(1); 470 | ``` 471 | 472 | #### 提示 473 | ```c++ 474 | #include 475 | #include 476 | using namespace std; 477 | 478 | const int MAX_N = 100; 479 | const int MAX_M = 10000; 480 | struct edge { 481 | int v, next; 482 | int len; 483 | } E[MAX_M]; 484 | int p[MAX_N], eid; 485 | void init() { 486 | memset(p, -1, sizeof(p)); 487 | eid = 0; 488 | } 489 | void insert(int u, int v, int len) { 490 | E[eid].v = v; 491 | E[eid].len = len; 492 | E[eid].next = p[u]; 493 | p[u] = eid++; 494 | } 495 | 496 | bool vst[MAX_N]; 497 | void dfs(int u) { 498 | cout << "visiting " << u << endl; 499 | vst[u] = true; 500 | for (int i = p[u]; i + 1; i= E[i].next) { 501 | if (!vst[E[i].v]) { 502 | dfs(E[i].v); 503 | } 504 | } 505 | } 506 | int main() { 507 | init(); 508 | insert(1, 2, 10); 509 | insert(1, 3, 5); 510 | insert(2, 3, 1); 511 | insert(3, 5, 10); 512 | insert(3, 4, 6); 513 | insert(5, 4, 11); 514 | memset(vst, false, sizeof(vst)); 515 | dfs(1); 516 | 517 | return 0; 518 | } 519 | ``` 520 | 521 | 522 | #### 代码 523 | ```c++ 524 | #include 525 | #include 526 | using namespace std; 527 | 528 | const int MAX_N = 100; 529 | const int MAX_M = 10000; 530 | struct edge { 531 | int v, next; 532 | int len; 533 | } E[MAX_M]; 534 | int p[MAX_N], eid; 535 | void init() { 536 | memset(p, -1, sizeof(p)); 537 | eid = 0; 538 | } 539 | void insert(int u, int v, int len) { 540 | E[eid].v = v; 541 | E[eid].len = len; 542 | E[eid].next = p[u]; 543 | p[u] = eid++; 544 | } 545 | 546 | bool vst[MAX_N]; 547 | void dfs(int u) { 548 | cout << "visiting " << u << endl; 549 | vst[u] = true; 550 | for (int i = p[u]; i + 1; i= E[i].next) { 551 | if (!vst[E[i].v]) { 552 | dfs(E[i].v); 553 | } 554 | } 555 | } 556 | int main() { 557 | init(); 558 | insert(1, 2, 10); 559 | insert(1, 3, 5); 560 | insert(2, 3, 1); 561 | insert(3, 5, 10); 562 | insert(3, 4, 6); 563 | insert(5, 4, 11); 564 | memset(vst, false, sizeof(vst)); 565 | dfs(1); 566 | 567 | return 0; 568 | } 569 | ``` 570 | 571 | 572 | 573 | --- 574 | ### 完成讲解 575 | 终于完成了,点击运行,看看效果吧。 576 | 577 | 聪明的你一定学会了如何使用`dfs`了。 578 | -------------------------------------------------------------------------------- /5_tree_grpah/code/1_adjacency_list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | const int MAX_N = 100; 6 | const int MAX_M = 10000; 7 | struct edge { 8 | int v, next; 9 | int len; 10 | } E[MAX_M]; 11 | int p[MAX_N], eid; 12 | void init() { 13 | memset(p, -1, sizeof(p)); 14 | eid = 0; 15 | } 16 | void insert(int u, int v, int len) { 17 | E[eid].v = v; 18 | E[eid].len = len; 19 | E[eid].next = p[u]; 20 | p[u] = eid++; 21 | } 22 | 23 | int main() { 24 | init(); 25 | insert(1, 2, 10); 26 | insert(1, 3, 5); 27 | insert(2, 3, 1); 28 | insert(3, 5, 10); 29 | insert(3, 4, 6); 30 | insert(5, 4, 11); 31 | for (int u = 1; u <= 5; ++u) { 32 | for (int i = p[u]; i + 1; i = E[i].next) { 33 | cout << "(" << u << ", " << E[i].v << ")" << endl; 34 | } 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /5_tree_grpah/code/2_graph_dfs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | const int MAX_N = 100; 6 | const int MAX_M = 10000; 7 | struct edge { 8 | int v, next; 9 | int len; 10 | } E[MAX_M]; 11 | int p[MAX_N], eid; 12 | void init() { 13 | memset(p, -1, sizeof(p)); 14 | eid = 0; 15 | } 16 | void insert(int u, int v, int len) { 17 | E[eid].v = v; 18 | E[eid].len = len; 19 | E[eid].next = p[u]; 20 | p[u] = eid++; 21 | } 22 | 23 | bool vst[MAX_N]; 24 | void dfs(int u) { 25 | cout << "visiting " << u << endl; 26 | vst[u] = true; 27 | for (int i = p[u]; i + 1; i= E[i].next) { 28 | if (!vst[E[i].v]) { 29 | dfs(E[i].v); 30 | } 31 | } 32 | } 33 | int main() { 34 | init(); 35 | insert(1, 2, 10); 36 | insert(1, 3, 5); 37 | insert(2, 3, 1); 38 | insert(3, 5, 10); 39 | insert(3, 4, 6); 40 | insert(5, 4, 11); 41 | memset(vst, false, sizeof(vst)); 42 | dfs(1); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /5_tree_grpah/code/3_graph_bfs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | const int MAX_N = 100; 6 | const int MAX_M = 10000; 7 | struct edge { 8 | int v, next; 9 | int len; 10 | } E[MAX_M]; 11 | int p[MAX_N], eid; 12 | void init() { 13 | memset(p, -1, sizeof(p)); 14 | eid = 0; 15 | } 16 | void insert(int u, int v, int len) { 17 | E[eid].v = v; 18 | E[eid].len = len; 19 | E[eid].next = p[u]; 20 | p[u] = eid++; 21 | } 22 | 23 | bool vst[MAX_N]; 24 | void bfs(int v) { 25 | memset(vst, false, sizeof(vst)); 26 | queue q; 27 | q.push(v); 28 | vst[v] = 1; 29 | while (!q.empty()) { 30 | int u = q.front(); 31 | q.pop(); 32 | cout << "visiting " << u << endl; 33 | for (int i = p[u]; i + 1; i = E[i].next) { 34 | if (!vst[E[i].v]) { 35 | vst[E[i].v] = 1; 36 | q.push(E[i].v); 37 | } 38 | } 39 | } 40 | } 41 | int main() { 42 | init(); 43 | insert(1, 2, 10); 44 | insert(1, 3, 5); 45 | insert(2, 3, 1); 46 | insert(3, 5, 10); 47 | insert(3, 4, 6); 48 | insert(5, 4, 11); 49 | bfs(1); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /5_tree_grpah/content.md: -------------------------------------------------------------------------------- 1 | # 图论基础模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|邻接表模板|1|| 7 | |跟随|图上 dfs 模板|1|| 8 | |跟随|图上 bfs 模板|1|| 9 | 10 | 图论基础。 11 | -------------------------------------------------------------------------------- /6_short_path/3_floyd.md: -------------------------------------------------------------------------------- 1 | # floyd 的使用 2 | - floyd 3 | 4 | ### 文件名 5 | floyd.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | using namespace std; 15 | 16 | int main() { 17 | 18 | return 0; 19 | } 20 | ``` 21 | 22 | --- 23 | ### 第一步 24 | #### 讲解 25 | 这一节我们学习`floyd`最短路算法。 26 | 问题:对于一个各边权值均大于零的有向图,对每一对顶点i!=j,求出顶点i和j之间的最短路径和最短路径长度。 27 | 可以通过以每个顶点作为源点循环求出每队顶点之间的最短路径。除此之外,也可用弗洛伊德(Floyd)算法求两顶点之间的最短路径。 28 | 我们先定义好需要用到的数据。在`main`函数上面写下 29 | ```c++ 30 | const int inf = 0x3f3f3f3f; 31 | int G[110][110]; 32 | int n; 33 | ``` 34 | 35 | #### 代码 36 | ```c++ 37 | #include 38 | using namespace std; 39 | 40 | const int inf = 0x3f3f3f3f; 41 | int G[110][110]; 42 | int n; 43 | int main() { 44 | 45 | return 0; 46 | } 47 | ``` 48 | 49 | --- 50 | ### 第二步 51 | #### 讲解 52 | 接下来我们输入顶点个数`n`和每对顶点之间的距离长度。 53 | 在`main`函数里面写下 54 | ```c++ 55 | cin >> n; 56 | for (int i = 0; i < n; ++i) { 57 | for (int j = 0; j < n; ++j) { 58 | cin >> G[i][j]; 59 | } 60 | } 61 | ``` 62 | 63 | #### 提示 64 | ```c++ 65 | #include 66 | using namespace std; 67 | 68 | const int inf = 0x3f3f3f3f; 69 | int G[110][110]; 70 | int n; 71 | int main() { 72 | cin >> n; 73 | for (int i = 0; i < n; ++i) { 74 | for (int j = 0; j < n; ++j) { 75 | cin >> G[i][j]; 76 | } 77 | } 78 | 79 | return 0; 80 | } 81 | ``` 82 | 83 | #### 代码 84 | ```c++ 85 | #include 86 | using namespace std; 87 | 88 | const int inf = 0x3f3f3f3f; 89 | int G[110][110]; 90 | int n; 91 | int main() { 92 | cin >> n; 93 | for (int i = 0; i < n; ++i) { 94 | for (int j = 0; j < n; ++j) { 95 | cin >> G[i][j]; 96 | } 97 | } 98 | 99 | return 0; 100 | } 101 | ``` 102 | 103 | --- 104 | ### 第三步 105 | #### 讲解 106 | 接下来实现最关键的`floyd()`函数。 107 | 弗洛伊德(Floyd)算法过程: 108 | 1、用`G[i][j]`记录每一对顶点的最短距离。 109 | 2、依次扫描每一个点,并以其为基点再遍历所有每一对顶点`G[i][j]`的值,看看是否可用过该基点让这对顶点间的距离更小。 110 | 从i号顶点到j号顶点只经过前k号点的最短路程。其实这是一种`动态规划`的思想。 111 | 在`main`函数上面继续写下 112 | ```c++ 113 | void floyd() { 114 | for (int k = 0; k < n; ++k) { 115 | for (int i = 0; i < n; ++i) { 116 | for (int j = 0; j < n; ++j) { 117 | if (G[i][k] + G[k][j] < G[i][j]) { 118 | G[i][j] = G[i][k] + G[k][j]; 119 | } 120 | } 121 | } 122 | } 123 | } 124 | ``` 125 | 126 | #### 提示 127 | ```c++ 128 | #include 129 | using namespace std; 130 | 131 | const int inf = 0x3f3f3f3f; 132 | int G[110][110]; 133 | int n; 134 | void floyd() { 135 | for (int k = 0; k < n; ++k) { 136 | for (int i = 0; i < n; ++i) { 137 | for (int j = 0; j < n; ++j) { 138 | if (G[i][k] + G[k][j] < G[i][j]) { 139 | G[i][j] = G[i][k] + G[k][j]; 140 | } 141 | } 142 | } 143 | } 144 | } 145 | int main() { 146 | cin >> n; 147 | for (int i = 0; i < n; ++i) { 148 | for (int j = 0; j < n; ++j) { 149 | cin >> G[i][j]; 150 | } 151 | } 152 | 153 | return 0; 154 | } 155 | ``` 156 | 157 | 158 | #### 代码 159 | ```c++ 160 | #include 161 | using namespace std; 162 | 163 | const int inf = 0x3f3f3f3f; 164 | int G[110][110]; 165 | int n; 166 | void floyd() { 167 | for (int k = 0; k < n; ++k) { 168 | for (int i = 0; i < n; ++i) { 169 | for (int j = 0; j < n; ++j) { 170 | if (G[i][k] + G[k][j] < G[i][j]) { 171 | G[i][j] = G[i][k] + G[k][j]; 172 | } 173 | } 174 | } 175 | } 176 | } 177 | int main() { 178 | cin >> n; 179 | for (int i = 0; i < n; ++i) { 180 | for (int j = 0; j < n; ++j) { 181 | cin >> G[i][j]; 182 | } 183 | } 184 | 185 | return 0; 186 | } 187 | ``` 188 | 189 | 190 | --- 191 | ### 第四步 192 | #### 讲解 193 | 然后调用`floyd`函数。 194 | 在`main`函数里继续写下 195 | ```c++ 196 | floyd(); 197 | ``` 198 | 199 | #### 提示 200 | ```c++ 201 | #include 202 | using namespace std; 203 | 204 | const int inf = 0x3f3f3f3f; 205 | int G[110][110]; 206 | int n; 207 | void floyd() { 208 | for (int k = 0; k < n; ++k) { 209 | for (int i = 0; i < n; ++i) { 210 | for (int j = 0; j < n; ++j) { 211 | if (G[i][k] + G[k][j] < G[i][j]) { 212 | G[i][j] = G[i][k] + G[k][j]; 213 | } 214 | } 215 | } 216 | } 217 | } 218 | int main() { 219 | cin >> n; 220 | for (int i = 0; i < n; ++i) { 221 | for (int j = 0; j < n; ++j) { 222 | cin >> G[i][j]; 223 | } 224 | } 225 | floyd(); 226 | 227 | return 0; 228 | } 229 | ``` 230 | 231 | 232 | #### 代码 233 | ```c++ 234 | #include 235 | using namespace std; 236 | 237 | const int inf = 0x3f3f3f3f; 238 | int G[110][110]; 239 | int n; 240 | void floyd() { 241 | for (int k = 0; k < n; ++k) { 242 | for (int i = 0; i < n; ++i) { 243 | for (int j = 0; j < n; ++j) { 244 | if (G[i][k] + G[k][j] < G[i][j]) { 245 | G[i][j] = G[i][k] + G[k][j]; 246 | } 247 | } 248 | } 249 | } 250 | } 251 | int main() { 252 | cin >> n; 253 | for (int i = 0; i < n; ++i) { 254 | for (int j = 0; j < n; ++j) { 255 | cin >> G[i][j]; 256 | } 257 | } 258 | floyd(); 259 | 260 | return 0; 261 | } 262 | ``` 263 | 264 | 265 | --- 266 | ### 第五步 267 | #### 讲解 268 | 最后输出每一对顶点之间的最短路径长度。 269 | 在`main`函数里继续写下 270 | ```c++ 271 | for (int i = 0; i < n; ++i) { 272 | for (int j = 0; j < n; ++j) { 273 | cout << G[i][j] << " "; 274 | } 275 | cout << endl; 276 | } 277 | ``` 278 | 279 | #### 提示 280 | ```c++ 281 | #include 282 | using namespace std; 283 | 284 | const int inf = 0x3f3f3f3f; 285 | int G[110][110]; 286 | int n; 287 | void floyd() { 288 | for (int k = 0; k < n; ++k) { 289 | for (int i = 0; i < n; ++i) { 290 | for (int j = 0; j < n; ++j) { 291 | if (G[i][k] + G[k][j] < G[i][j]) { 292 | G[i][j] = G[i][k] + G[k][j]; 293 | } 294 | } 295 | } 296 | } 297 | } 298 | int main() { 299 | cin >> n; 300 | for (int i = 0; i < n; ++i) { 301 | for (int j = 0; j < n; ++j) { 302 | cin >> G[i][j]; 303 | } 304 | } 305 | floyd(); 306 | for (int i = 0; i < n; ++i) { 307 | for (int j = 0; j < n; ++j) { 308 | cout << G[i][j] << " "; 309 | } 310 | cout << endl; 311 | } 312 | return 0; 313 | } 314 | ``` 315 | 316 | 317 | #### 代码 318 | ```c++ 319 | #include 320 | using namespace std; 321 | 322 | const int inf = 0x3f3f3f3f; 323 | int G[110][110]; 324 | int n; 325 | void floyd() { 326 | for (int k = 0; k < n; ++k) { 327 | for (int i = 0; i < n; ++i) { 328 | for (int j = 0; j < n; ++j) { 329 | if (G[i][k] + G[k][j] < G[i][j]) { 330 | G[i][j] = G[i][k] + G[k][j]; 331 | } 332 | } 333 | } 334 | } 335 | } 336 | int main() { 337 | cin >> n; 338 | for (int i = 0; i < n; ++i) { 339 | for (int j = 0; j < n; ++j) { 340 | cin >> G[i][j]; 341 | } 342 | } 343 | floyd(); 344 | for (int i = 0; i < n; ++i) { 345 | for (int j = 0; j < n; ++j) { 346 | cout << G[i][j] << " "; 347 | } 348 | cout << endl; 349 | } 350 | return 0; 351 | } 352 | ``` 353 | 354 | 355 | --- 356 | ### 完成讲解 357 | 终于完成了,点击运行,输入下面的数据看看效果吧。 358 | ``` 359 | 4 360 | 0 3 1 4 361 | 1 0 3 5 362 | 10 2 0 7 363 | 1 2 3 0 364 | ``` 365 | -------------------------------------------------------------------------------- /6_short_path/code/1_dij.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | const int inf = 0x3f3f3f3f; 5 | int G[110][110]; 6 | int dist[110]; 7 | bool vst[110]; 8 | int n, m; 9 | void dijkstra(int s) { 10 | memset(vst, false, sizeof(vst)); 11 | memset(dist, 0x3f, sizeof(dist)); 12 | dist[s] = 0; 13 | for (int i = 0; i < n; ++i) { 14 | int v, min_d = inf; 15 | for (int j = 1; j <= n; ++j) { 16 | if (!vst[j] && dist[j] < min_d) { 17 | min_d = dist[j]; 18 | v = j; 19 | } 20 | } 21 | vst[v] = true; 22 | for (int j = 1; j <= n; ++j) { 23 | dist[j] = min(dist[j], dist[v] + G[v][j]); 24 | } 25 | } 26 | } 27 | int main() { 28 | cin >> n >> m; 29 | memset(G, 0x3f, sizeof(G)); 30 | for (int i = 0; i < m; ++i) { 31 | int u, v, len; 32 | cin >> u >> v >> len; 33 | G[u][v] = G[v][u] = len; 34 | } 35 | dijkstra(1); 36 | for (int i = 1; i <= n; ++i) { 37 | cout << dist[i] << " "; 38 | } 39 | return 0; 40 | } 41 | 42 | /* 43 | 5 6 44 | 1 2 3 45 | 2 3 2 46 | 3 4 1 47 | 2 4 10 48 | 4 5 1 49 | 1 3 7 50 | */ 51 | -------------------------------------------------------------------------------- /6_short_path/code/2_spfa.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | const int MAX_N = 100; 6 | const int MAX_M = 10000; 7 | struct edge { 8 | int v, next; 9 | int len; 10 | } E[MAX_M]; 11 | int p[MAX_N], eid; 12 | void init() { 13 | memset(p, -1, sizeof(p)); 14 | eid = 0; 15 | } 16 | void insert(int u, int v, int len) { 17 | E[eid].v = v; 18 | E[eid].len = len; 19 | E[eid].next = p[u]; 20 | p[u] = eid++; 21 | } 22 | bool inq[MAX_N]; 23 | int d[MAX_N]; 24 | void spfa(int s) { 25 | memset(inq, false, sizeof(inq)); 26 | memset(d, 0x3f, sizeof(d)); 27 | d[s] = 0; 28 | inq[s] = true; 29 | queue q; 30 | q.push(s); 31 | while (!q.empty()) { 32 | int u = q.front(); 33 | q.pop(); 34 | inq[u] = false; 35 | for (int i = p[u]; i + 1; i = E[i].next) { 36 | int v = E[i].v; 37 | if (d[u] + E[i].len < d[v]) { 38 | d[v] = d[u] + E[i].len; 39 | if (!inq[v]) { 40 | q.push(v); 41 | inq[v] = true; 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | int main() { 49 | int n, m; 50 | cin >> n >> m; 51 | init(); 52 | for (int i = 0; i < m; ++i) { 53 | int u, v, len; 54 | cin >> u >> v >> len; 55 | insert(u, v, len); 56 | insert(v, u, len); 57 | } 58 | spfa(1); 59 | for (int i = 1; i <= n; ++i) { 60 | cout << d[i] << " "; 61 | } 62 | return 0; 63 | } 64 | /* 65 | 5 6 66 | 1 2 3 67 | 2 3 2 68 | 3 4 1 69 | 2 4 10 70 | 4 5 1 71 | 1 3 7 72 | */ 73 | -------------------------------------------------------------------------------- /6_short_path/code/3_floyd.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | const int inf = 0x3f3f3f3f; 5 | int G[110][110]; 6 | int n; 7 | void floyd() { 8 | for (int k = 0; k < n; ++k) { 9 | for (int i = 0; i < n; ++i) { 10 | for (int j = 0; j < n; ++j) { 11 | if (G[i][k] + G[k][j] < G[i][j]) { 12 | G[i][j] = G[i][k] + G[k][j]; 13 | } 14 | } 15 | } 16 | } 17 | } 18 | int main() { 19 | cin >> n; 20 | for (int i = 0; i < n; ++i) { 21 | for (int j = 0; j < n; ++j) { 22 | cin >> G[i][j]; 23 | } 24 | } 25 | floyd(); 26 | for (int i = 0; i < n; ++i) { 27 | for (int j = 0; j < n; ++j) { 28 | cout << G[i][j] << " "; 29 | } 30 | cout << endl; 31 | } 32 | return 0; 33 | } 34 | 35 | /* 36 | 4 37 | 0 3 1 4 38 | 1 0 3 5 39 | 10 2 0 7 40 | 1 2 3 0 41 | */ 42 | -------------------------------------------------------------------------------- /6_short_path/content.md: -------------------------------------------------------------------------------- 1 | # 最短路模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|Dijkstra 模板|1|| 7 | |跟随|spfa 模板|1|| 8 | |跟随|floyd 模板|1|| 9 | 10 | 最短路。 11 | -------------------------------------------------------------------------------- /7_graph_algorithm/code/1_kruskal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | const int MAX_N = 100000; 8 | const int MAX_M = 100000; 9 | 10 | struct edge { 11 | int u, v, w; 12 | bool operator < (const edge &a) const { 13 | return w < a.w; 14 | } 15 | } e[MAX_M]; 16 | 17 | int fa[MAX_N], n, m; 18 | int get(int x) { 19 | if(fa[x] == x) { 20 | return fa[x]; 21 | } 22 | return fa[x] = get(fa[x]); 23 | } 24 | 25 | int main() { 26 | cin >> n >> m; 27 | for (int i = 0; i < m; ++i) { 28 | cin >> e[i].u >> e[i].v >> e[i].w; 29 | } 30 | sort(e, e + m); 31 | for (int i = 1; i <= n; ++i) { 32 | fa[i] = i; 33 | } 34 | int sum = 0; 35 | for (int i = 0; i < m; ++i) { 36 | int x = get(e[i].u), y = get(e[i].v); 37 | if (x != y) { 38 | fa[x] = y; 39 | sum += e[i].w; 40 | } 41 | } 42 | cout << sum << endl; 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /7_graph_algorithm/code/2_lca.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | const int MAX_N = 100000; 6 | const int MAX_M = 1000000; 7 | 8 | struct edge { 9 | int v, next; 10 | } E[MAX_M]; 11 | int p[MAX_N], eid; 12 | void init() { 13 | memset(p, -1, sizeof(p)); 14 | eid = 0; 15 | } 16 | void insert(int u, int v) { 17 | E[eid].v = v; 18 | E[eid].next = p[u]; 19 | p[u] = eid++; 20 | } 21 | int d[MAX_N], fa[MAX_N][20]; 22 | void dfs(int u) { 23 | for (int i = p[u]; i + 1; i = E[i].next) { 24 | if (d[E[i].v] == -1) { 25 | d[E[i].v] = d[u] + 1; 26 | fa[E[i].v][0] = u; 27 | dfs(E[i].v); 28 | } 29 | } 30 | } 31 | int lca(int x, int y) { 32 | int i, j; 33 | if (d[x] < d[y]) { 34 | swap(x, y); 35 | } 36 | for (i = 0; (1 << i) <= d[x]; i++); 37 | --i; 38 | for (j = i; j >= 0; j--) { 39 | if (d[x] - (1 << j) >= d[y]) { 40 | x = fa[x][j]; 41 | } 42 | } 43 | if (x == y) { 44 | return x; 45 | } 46 | for (j = i; j >= 0; j--) { 47 | if (fa[x][j] != fa[y][j]) { 48 | x = fa[x][j]; 49 | y = fa[y][j]; 50 | } 51 | } 52 | return fa[x][0]; 53 | } 54 | int main() { 55 | int n; 56 | init(); 57 | cin >> n; 58 | for (int i = 0; i < n - 1; ++i) { 59 | int u, v; 60 | cin >> u >> v; 61 | insert(u, v); 62 | insert(v, u); 63 | } 64 | memset(d, -1, sizeof(d)); 65 | dfs(1); 66 | for (int level = 1; (1 << level) <= n; level++) { 67 | for (int i = 1; i <= n; i++) { 68 | fa[i][level] = fa[fa[i][level - 1]][level - 1]; 69 | } 70 | } 71 | int q; 72 | cin >> q; 73 | while (q--) { 74 | int a, b; 75 | cin >> a >> b; 76 | cout << lca(a, b) << endl; 77 | } 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /7_graph_algorithm/code/3_topsort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | const int MAX_N = 100; 6 | const int MAX_M = 10000; 7 | struct edge { 8 | int v, next; 9 | int len; 10 | } E[MAX_M]; 11 | int p[MAX_N], eid; 12 | void init() { 13 | memset(p, -1, sizeof(p)); 14 | eid = 0; 15 | } 16 | void insert(int u, int v) { 17 | E[eid].v = v; 18 | E[eid].next = p[u]; 19 | p[u] = eid++; 20 | } 21 | int n, m; 22 | int indegree[MAX_N]; 23 | void topo() { 24 | queue q; 25 | for (int i = 1; i <= n; i++) { 26 | if (indegree[i] == 0) { 27 | q.push(i); 28 | } 29 | } 30 | while (!q.empty()) { 31 | int now = q.front(); 32 | cout << now << endl; 33 | q.pop(); 34 | for (int i = p[now]; i + 1; i = E[i].next) { 35 | int v = E[i].v; 36 | indegree[v]--; 37 | if (indegree[v] == 0) { 38 | q.push(v); 39 | } 40 | } 41 | } 42 | } 43 | 44 | int main() { 45 | init(); 46 | memset(indegree, 0, sizeof(indegree)); 47 | cin >> n >> m; 48 | for (int i = 0; i < m; ++i) { 49 | int u, v; 50 | cin >> u >> v; 51 | insert(u, v); 52 | ++indegree[v]; 53 | } 54 | topo(); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /7_graph_algorithm/code/4_tarjan.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | const int MAX_N = 100; 6 | const int MAX_M = 10000; 7 | struct edge { 8 | int v, next; 9 | int len; 10 | } E[MAX_M]; 11 | int p[MAX_N], eid; 12 | void init() { 13 | memset(p, -1, sizeof(p)); 14 | eid = 0; 15 | } 16 | void insert(int u, int v) { 17 | E[eid].v = v; 18 | E[eid].next = p[u]; 19 | p[u] = eid++; 20 | } 21 | int dfn[MAX_N], low[MAX_N]; 22 | int idx = 0; 23 | int belong[MAX_N], scc = 0; 24 | int s[MAX_N], top = 0; 25 | bool in_stack[MAX_N]; 26 | 27 | void tarjan(int u) { 28 | dfn[u] = low[u] = ++idx; 29 | s[top++] = u; 30 | in_stack[u] = true; 31 | for (int i = p[u]; i + 1; i = E[i].next) { 32 | int v = E[i].v; 33 | if (!dfn[v]) { 34 | tarjan(v); 35 | low[u] = min(low[u], low[v]); 36 | } else if (in_stack[v]) { 37 | low[u] = min(low[u], dfn[v]); 38 | } 39 | } 40 | if (dfn[u] == low[u]) { 41 | ++scc; 42 | do { 43 | belong[s[--top]] = scc; 44 | in_stack[s[top]] = false; 45 | } while (s[top] != u); 46 | } 47 | } 48 | 49 | int main() { 50 | int n, m; 51 | init(); 52 | cin >> n >> m; 53 | for (int i = 0; i < m; ++i) { 54 | int u, v; 55 | cin >> u >> v; 56 | insert(u, v); 57 | } 58 | memset(dfn, 0, sizeof(dfn)); 59 | idx = 0; 60 | for (int i = 1; i <= n; ++i) { 61 | if (!dfn[i]) { 62 | tarjan(i); 63 | } 64 | } 65 | for (int i = 1; i <= scc; ++i) { 66 | cout << "block " << i << ": "; 67 | for (int j = 1; j <= n; ++j) { 68 | if (belong[j] == i) { 69 | cout << j << " "; 70 | } 71 | } 72 | cout << endl; 73 | } 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /7_graph_algorithm/content.md: -------------------------------------------------------------------------------- 1 | # 图论基础算法模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|Kruskal 模板|1|| 7 | |跟随|Lca 倍增算法模板|1|| 8 | |跟随|拓扑排序模板|1|| 9 | |跟随|Tarjan 算法模板|1|| 10 | 11 | 图论基础算法。 12 | -------------------------------------------------------------------------------- /8_match_and_flow/1_hungury.md: -------------------------------------------------------------------------------- 1 | # hungury 的使用 2 | - hungury 3 | 4 | ### 文件名 5 | hungury.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | int main() { 18 | 19 | return 0; 20 | } 21 | ``` 22 | 23 | --- 24 | ### 第一步 25 | #### 讲解 26 | 这一节我们学习二分图的最大匹配问题`hungury`(匈牙利算法)。 27 | 二分图:是图论中的一种特殊模型。若能将无向图G=(V,E)的顶点V划分为两个交集为空的顶点集,并且任意边的两个端点都分属于两个集合,则称图G为一个为二分图。 28 | 交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边...形成的路径叫交替路。 29 | 增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。 30 | 过程:由增广路的性质,增广路中的匹配边总是比未匹配边多一条,所以如果我们放弃一条增广路中的匹配边,选取未匹配边作为匹配边,则匹配的数量就会增加。匈牙利算法就是在不断寻找增广路,如果找不到增广路,就说明达到了最大匹配。 31 | 我们先定义好的要用到的数据。在`main`函数上面写下 32 | ```c++ 33 | bool G[110][110]; 34 | bool vis[110]; 35 | int link[110]; 36 | int n; 37 | ``` 38 | 39 | #### 代码 40 | ```c++ 41 | #include 42 | #include 43 | using namespace std; 44 | bool G[110][110]; 45 | bool vis[110]; 46 | int link[110]; 47 | int n; 48 | 49 | int main() { 50 | 51 | return 0; 52 | } 53 | ``` 54 | 55 | --- 56 | ### 第二步 57 | #### 讲解 58 | 接下来初始化邻接矩阵`G`。 59 | 在`main`函数里面写下 60 | ```c++ 61 | memset(G, false, sizeof(G)); 62 | ``` 63 | 64 | #### 提示 65 | ```c++ 66 | #include 67 | #include 68 | using namespace std; 69 | bool G[110][110]; 70 | bool vis[110]; 71 | int link[110]; 72 | int n; 73 | 74 | int main() { 75 | memset(G, false, sizeof(G)); 76 | 77 | return 0; 78 | } 79 | ``` 80 | 81 | #### 代码 82 | ```c++ 83 | #include 84 | #include 85 | using namespace std; 86 | bool G[110][110]; 87 | bool vis[110]; 88 | int link[110]; 89 | int n; 90 | 91 | int main() { 92 | memset(G, false, sizeof(G)); 93 | 94 | return 0; 95 | } 96 | ``` 97 | 98 | --- 99 | ### 第三步 100 | #### 讲解 101 | 然后输入图的顶点数`n`和边的条数`m`。 102 | 在`main`函数里面继续写下 103 | ```c++ 104 | int m; 105 | cin >> n >> m; 106 | ``` 107 | 108 | #### 提示 109 | ```c++ 110 | #include 111 | #include 112 | using namespace std; 113 | bool G[110][110]; 114 | bool vis[110]; 115 | int link[110]; 116 | int n; 117 | 118 | int main() { 119 | memset(G, false, sizeof(G)); 120 | int m; 121 | cin >> n >> m; 122 | 123 | return 0; 124 | } 125 | ``` 126 | 127 | 128 | #### 代码 129 | ```c++ 130 | #include 131 | #include 132 | using namespace std; 133 | bool G[110][110]; 134 | bool vis[110]; 135 | int link[110]; 136 | int n; 137 | 138 | int main() { 139 | memset(G, false, sizeof(G)); 140 | int m; 141 | cin >> n >> m; 142 | 143 | return 0; 144 | } 145 | ``` 146 | 147 | 148 | --- 149 | ### 第四步 150 | #### 讲解 151 | 接下来输入`m`条边的数据并将连通的两个点的值置为1。 152 | 在`main`函数里面写下 153 | ```c++ 154 | while (m--) { 155 | int a, b; 156 | cin >> a >> b; 157 | G[a][b] = 1; 158 | } 159 | ``` 160 | 161 | #### 提示 162 | ```c++ 163 | #include 164 | #include 165 | using namespace std; 166 | bool G[110][110]; 167 | bool vis[110]; 168 | int link[110]; 169 | int n; 170 | 171 | int main() { 172 | memset(G, false, sizeof(G)); 173 | int m; 174 | cin >> n >> m; 175 | while (m--) { 176 | int a, b; 177 | cin >> a >> b; 178 | G[a][b] = 1; 179 | } 180 | 181 | return 0; 182 | } 183 | ``` 184 | 185 | 186 | #### 代码 187 | ```c++ 188 | #include 189 | #include 190 | using namespace std; 191 | bool G[110][110]; 192 | bool vis[110]; 193 | int link[110]; 194 | int n; 195 | 196 | int main() { 197 | memset(G, false, sizeof(G)); 198 | int m; 199 | cin >> n >> m; 200 | while (m--) { 201 | int a, b; 202 | cin >> a >> b; 203 | G[a][b] = 1; 204 | } 205 | 206 | return 0; 207 | } 208 | ``` 209 | 210 | 211 | --- 212 | ### 第五步 213 | #### 讲解 214 | 接下来实现`match(int u)`函数。 215 | 这个过程是在寻找增光路进行匹配。 216 | 在`main`函数上面写下 217 | ```c++ 218 | bool match(int u) { 219 | for (int i = 1; i <= n; ++i) { 220 | if (G[u][i] && !vis[i]) { 221 | vis[i] = true; 222 | if (link[i] == -1 || match(link[i])) { 223 | link[i] = u; 224 | return true; 225 | } 226 | } 227 | } 228 | return false; 229 | } 230 | ``` 231 | 232 | #### 提示 233 | ```c++ 234 | #include 235 | #include 236 | using namespace std; 237 | bool G[110][110]; 238 | bool vis[110]; 239 | int link[110]; 240 | int n; 241 | bool match(int u) { 242 | for (int i = 1; i <= n; ++i) { 243 | if (G[u][i] && !vis[i]) { 244 | vis[i] = true; 245 | if (link[i] == -1 || match(link[i])) { 246 | link[i] = u; 247 | return true; 248 | } 249 | } 250 | } 251 | return false; 252 | } 253 | 254 | int main() { 255 | memset(G, false, sizeof(G)); 256 | int m; 257 | cin >> n >> m; 258 | while (m--) { 259 | int a, b; 260 | cin >> a >> b; 261 | G[a][b] = 1; 262 | } 263 | 264 | return 0; 265 | } 266 | ``` 267 | 268 | 269 | #### 代码 270 | ```c++ 271 | #include 272 | #include 273 | using namespace std; 274 | bool G[110][110]; 275 | bool vis[110]; 276 | int link[110]; 277 | int n; 278 | bool match(int u) { 279 | for (int i = 1; i <= n; ++i) { 280 | if (G[u][i] && !vis[i]) { 281 | vis[i] = true; 282 | if (link[i] == -1 || match(link[i])) { 283 | link[i] = u; 284 | return true; 285 | } 286 | } 287 | } 288 | return false; 289 | } 290 | 291 | int main() { 292 | memset(G, false, sizeof(G)); 293 | int m; 294 | cin >> n >> m; 295 | while (m--) { 296 | int a, b; 297 | cin >> a >> b; 298 | G[a][b] = 1; 299 | } 300 | 301 | return 0; 302 | } 303 | ``` 304 | 305 | 306 | --- 307 | ### 第六步 308 | #### 讲解 309 | 接下来实现`hungury()`函数。 310 | 需要对于每个点都去找一下看是否有增广路达到更大的匹配。 311 | 在`main`函数上面写下 312 | ```c++ 313 | int hungury() { 314 | int ans = 0; 315 | memset(link, -1, sizeof(link)); 316 | for (int i = 1; i <= n; ++i) { 317 | memset(vis, false, sizeof(vis)); 318 | ans += match(i); 319 | } 320 | return ans; 321 | } 322 | ``` 323 | 324 | #### 提示 325 | ```c++ 326 | #include 327 | #include 328 | using namespace std; 329 | bool G[110][110]; 330 | bool vis[110]; 331 | int link[110]; 332 | int n; 333 | bool match(int u) { 334 | for (int i = 1; i <= n; ++i) { 335 | if (G[u][i] && !vis[i]) { 336 | vis[i] = true; 337 | if (link[i] == -1 || match(link[i])) { 338 | link[i] = u; 339 | return true; 340 | } 341 | } 342 | } 343 | return false; 344 | } 345 | int hungury() { 346 | int ans = 0; 347 | memset(link, -1, sizeof(link)); 348 | for (int i = 1; i <= n; ++i) { 349 | memset(vis, false, sizeof(vis)); 350 | ans += match(i); 351 | } 352 | return ans; 353 | } 354 | 355 | int main() { 356 | memset(G, false, sizeof(G)); 357 | int m; 358 | cin >> n >> m; 359 | while (m--) { 360 | int a, b; 361 | cin >> a >> b; 362 | G[a][b] = 1; 363 | } 364 | 365 | return 0; 366 | } 367 | ``` 368 | 369 | 370 | #### 代码 371 | ```c++ 372 | #include 373 | #include 374 | using namespace std; 375 | bool G[110][110]; 376 | bool vis[110]; 377 | int link[110]; 378 | int n; 379 | bool match(int u) { 380 | for (int i = 1; i <= n; ++i) { 381 | if (G[u][i] && !vis[i]) { 382 | vis[i] = true; 383 | if (link[i] == -1 || match(link[i])) { 384 | link[i] = u; 385 | return true; 386 | } 387 | } 388 | } 389 | return false; 390 | } 391 | int hungury() { 392 | int ans = 0; 393 | memset(link, -1, sizeof(link)); 394 | for (int i = 1; i <= n; ++i) { 395 | memset(vis, false, sizeof(vis)); 396 | ans += match(i); 397 | } 398 | return ans; 399 | } 400 | 401 | int main() { 402 | memset(G, false, sizeof(G)); 403 | int m; 404 | cin >> n >> m; 405 | while (m--) { 406 | int a, b; 407 | cin >> a >> b; 408 | G[a][b] = 1; 409 | } 410 | 411 | return 0; 412 | } 413 | ``` 414 | 415 | 416 | --- 417 | ### 第七步 418 | #### 讲解 419 | 最后调用`hungury()`输出计算结果。 420 | 在`main`函数里面写下 421 | ```c++ 422 | cout << hungury() << endl; 423 | ``` 424 | 425 | #### 提示 426 | ```c++ 427 | #include 428 | #include 429 | using namespace std; 430 | bool G[110][110]; 431 | bool vis[110]; 432 | int link[110]; 433 | int n; 434 | bool match(int u) { 435 | for (int i = 1; i <= n; ++i) { 436 | if (G[u][i] && !vis[i]) { 437 | vis[i] = true; 438 | if (link[i] == -1 || match(link[i])) { 439 | link[i] = u; 440 | return true; 441 | } 442 | } 443 | } 444 | return false; 445 | } 446 | int hungury() { 447 | int ans = 0; 448 | memset(link, -1, sizeof(link)); 449 | for (int i = 1; i <= n; ++i) { 450 | memset(vis, false, sizeof(vis)); 451 | ans += match(i); 452 | } 453 | return ans; 454 | } 455 | 456 | int main() { 457 | memset(G, false, sizeof(G)); 458 | int m; 459 | cin >> n >> m; 460 | while (m--) { 461 | int a, b; 462 | cin >> a >> b; 463 | G[a][b] = 1; 464 | } 465 | cout << hungury() << endl; 466 | return 0; 467 | } 468 | ``` 469 | 470 | 471 | #### 代码 472 | ```c++ 473 | #include 474 | #include 475 | using namespace std; 476 | bool G[110][110]; 477 | bool vis[110]; 478 | int link[110]; 479 | int n; 480 | bool match(int u) { 481 | for (int i = 1; i <= n; ++i) { 482 | if (G[u][i] && !vis[i]) { 483 | vis[i] = true; 484 | if (link[i] == -1 || match(link[i])) { 485 | link[i] = u; 486 | return true; 487 | } 488 | } 489 | } 490 | return false; 491 | } 492 | int hungury() { 493 | int ans = 0; 494 | memset(link, -1, sizeof(link)); 495 | for (int i = 1; i <= n; ++i) { 496 | memset(vis, false, sizeof(vis)); 497 | ans += match(i); 498 | } 499 | return ans; 500 | } 501 | 502 | int main() { 503 | memset(G, false, sizeof(G)); 504 | int m; 505 | cin >> n >> m; 506 | while (m--) { 507 | int a, b; 508 | cin >> a >> b; 509 | G[a][b] = 1; 510 | } 511 | cout << hungury() << endl; 512 | return 0; 513 | } 514 | ``` 515 | 516 | 517 | --- 518 | ### 完成讲解 519 | 终于完成了,点击运行,看看效果吧。 520 | 521 | 聪明的你一定学会了`hungury算法`了。 522 | -------------------------------------------------------------------------------- /8_match_and_flow/code/1_hungury.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | bool G[110][110]; 5 | bool vis[110]; 6 | int link[110]; 7 | int n; 8 | bool match(int u) { 9 | for (int i = 1; i <= n; ++i) { 10 | if (G[u][i] && !vis[i]) { 11 | vis[i] = true; 12 | if (link[i] == -1 || match(link[i])) { 13 | link[i] = u; 14 | return true; 15 | } 16 | } 17 | } 18 | return false; 19 | } 20 | int hungury() { 21 | int ans = 0; 22 | memset(link, -1, sizeof(link)); 23 | for (int i = 1; i <= n; ++i) { 24 | memset(vis, false, sizeof(vis)); 25 | ans += match(i); 26 | } 27 | return ans; 28 | } 29 | int main() { 30 | memset(G, false, sizeof(G)); 31 | int m; 32 | cin >> n >> m; 33 | while (m--) { 34 | int a, b; 35 | cin >> a >> b; 36 | G[a][b] = 1; 37 | } 38 | cout << hungury() << endl; 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /8_match_and_flow/code/2_dinic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | const int INF = 0x3f3f3f3f; 6 | const int MAX_N = 100; 7 | const int MAX_M = 10000; 8 | struct edge { 9 | int v, c, next; 10 | } e[MAX_M]; 11 | int p[MAX_N], eid; 12 | void init() { 13 | memset(p, -1, sizeof(p)); 14 | eid = 0; 15 | } 16 | void insert(int u, int v, int c) { 17 | e[eid].v = v; 18 | e[eid].next = p[u]; 19 | e[eid].c = c; 20 | p[u] = eid++; 21 | } 22 | void addedge(int u, int v, int c) { 23 | insert(u, v, c); 24 | insert(v, u, 0); 25 | } 26 | int S, T; 27 | int d[MAX_N]; 28 | bool bfs() { 29 | memset(d, -1, sizeof(d)); 30 | queue q; 31 | q.push(S); 32 | d[S] = 0; 33 | while (!q.empty()) { 34 | int u = q.front(); 35 | q.pop(); 36 | for (int i = p[u]; i != -1; i = e[i].next) { 37 | int v = e[i].v; 38 | if (e[i].c > 0 && d[v] == -1) { 39 | q.push(v); 40 | d[v] = d[u] + 1; 41 | } 42 | } 43 | } 44 | return (d[T] != -1); 45 | } 46 | 47 | int dfs(int u, int flow) { 48 | if (u == T) { 49 | return flow; 50 | } 51 | int res = 0; 52 | for (int i = p[u]; i != -1; i = e[i].next) { 53 | int v = e[i].v; 54 | if (e[i].c > 0 && d[u] + 1 == d[v]) { 55 | int tmp = dfs(v, min(flow, e[i].c)); 56 | flow -= tmp; 57 | e[i].c -= tmp; 58 | res += tmp; 59 | e[i ^ 1].c += tmp; 60 | if (flow == 0) { 61 | break; 62 | } 63 | } 64 | } 65 | if (res == 0) { 66 | d[u] = -1; 67 | } 68 | return res; 69 | } 70 | 71 | int dinic() { 72 | int res = 0; 73 | while (bfs()) { 74 | res += dfs(S, INF); 75 | } 76 | return res; 77 | } 78 | 79 | int main() { 80 | int n, m; 81 | init(); 82 | cin >> n >> m; 83 | for (int i = 0; i < m; ++i) { 84 | int u, v, flow; 85 | cin >> u >> v >> flow; 86 | addedge(u, v, flow); 87 | } 88 | cin >> S >> T; 89 | cout << dinic() << endl; 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /8_match_and_flow/code/3_cost_flow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | const int MAX_N = 1000; 7 | const int MAX_M = 10000; 8 | const int inf = 0x3f3f3f3f; 9 | 10 | struct edge { 11 | int v, c, w, next; 12 | } e[MAX_M]; 13 | int p[MAX_N], s, t, eid; 14 | void init() { 15 | memset(p, -1, sizeof(p)); 16 | eid = 0; 17 | } 18 | void insert(int u, int v, int c, int w) { 19 | e[eid].v = v; 20 | e[eid].c = c; 21 | e[eid].w = w; 22 | e[eid].next = p[u]; 23 | p[u] = eid++; 24 | } 25 | void addedge(int u, int v, int c, int w) { 26 | insert(u, v, c, w); 27 | insert(v, u, 0, -w); 28 | } 29 | bool inq[MAX_N]; 30 | int d[MAX_N]; 31 | int pre[MAX_N]; 32 | bool spfa() { 33 | memset(inq, 0, sizeof(inq)); 34 | memset(d, 0x3f, sizeof(d)); 35 | memset(pre, -1, sizeof(pre)); 36 | d[s] = 0; 37 | inq[s] = true; 38 | queue q; 39 | q.push(s); 40 | while (!q.empty()) { 41 | int u = q.front(); 42 | q.pop(); 43 | inq[u] = false; 44 | for (int i = p[u]; i != -1; i = e[i].next) { 45 | if (e[i].c) { 46 | int v = e[i].v; 47 | if (d[u] + e[i].w < d[v]) { 48 | d[v] = d[u] + e[i].w; 49 | pre[v] = i; 50 | if (!inq[v]) { 51 | q.push(v); 52 | inq[v] = true; 53 | } 54 | } 55 | } 56 | } 57 | } 58 | return pre[t] != -1; 59 | } 60 | 61 | int costflow() { 62 | int ret = 0; 63 | while(spfa()) { 64 | int flow = inf; 65 | for(int i = t; i != s; i = e[pre[i]^1].v) { 66 | flow = min(e[pre[i]].c, flow); 67 | } 68 | for(int i = t; i != s; i = e[pre[i]^1].v) { 69 | e[pre[i]].c -= flow; 70 | e[pre[i]^1].c += flow; 71 | ret += e[pre[i]].w * flow; 72 | } 73 | } 74 | return ret; 75 | } 76 | 77 | int main() { 78 | int n, m; 79 | init(); 80 | cin >> n >> m; 81 | for (int i = 0; i < m; ++i) { 82 | int u, v, c, w; 83 | cin >> u >> v >> c >> w; 84 | addedge(u, v, c, w); 85 | } 86 | cin >> s >> t; 87 | cout << costflow() << endl; 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /8_match_and_flow/content.md: -------------------------------------------------------------------------------- 1 | # 匹配和网络流模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|匈牙利算法模板|1|| 7 | |跟随|Dinic 算法模板|1|| 8 | |跟随|最小费用最大流模板|1|| 9 | 10 | 匹配和网络流。 11 | -------------------------------------------------------------------------------- /9_string_basic/1_kmp.md: -------------------------------------------------------------------------------- 1 | # kmp 的使用 2 | - kmp 3 | 4 | ### 文件名 5 | kmp.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | int main() { 18 | 19 | return 0; 20 | } 21 | ``` 22 | 23 | --- 24 | ### 第一步 25 | #### 讲解 26 | 这一节我们学习字符串模式匹配(KMP)算法。 27 | KMP算法的思想是:设s为目标串,t为模式串,并设i指针和j指针分别指示目标串和模式串中正待比较的字符,令i和j的初始值均为0。若有si=tj,则i和j分别增1;否则,i不变,j退回到j=next[j]的位置(即模式串右滑),比较si和tj,若相等则指针各增1,否则j再退回到下一个j=next[j]的位置(即模式串继续右滑),再比较si和tj。依次类推,直到出现下列两种情况之一为止:一种情况是j退回到某个j=next[j]位置时有si=tj,则指针各增1后继续匹配;另一种情况是j退回到j=-1,此时令i、j指针各增1,即从s(i+1)和t0开始继续匹配。 28 | 我们先定义`Next`数组。在`main`函数上面写下 29 | ```c++ 30 | int Next[1110]; 31 | ``` 32 | 33 | #### 代码 34 | ```c++ 35 | #include 36 | #include 37 | using namespace std; 38 | int Next[1110]; 39 | 40 | int main() { 41 | 42 | return 0; 43 | } 44 | ``` 45 | 46 | --- 47 | ### 第二步 48 | #### 讲解 49 | 接下来输入字符串`s`和`T`。 50 | 在`main`函数里面写下 51 | ```c++ 52 | char s[1100], T[1100]; 53 | cin >> T >> s; 54 | ``` 55 | 56 | #### 提示 57 | ```c++ 58 | #include 59 | #include 60 | using namespace std; 61 | int Next[1110]; 62 | 63 | int main() { 64 | char s[1100], T[1100]; 65 | cin >> T >> s; 66 | 67 | return 0; 68 | } 69 | ``` 70 | 71 | #### 代码 72 | ```c++ 73 | #include 74 | #include 75 | using namespace std; 76 | int Next[1110]; 77 | 78 | int main() { 79 | char s[1100], T[1100]; 80 | cin >> T >> s; 81 | 82 | return 0; 83 | } 84 | ``` 85 | 86 | --- 87 | ### 第三步 88 | #### 讲解 89 | 然后实现`get_Next(char *p)`函数。 90 | 预处理出`Next`数组是算法的关键。 91 | 在`main`函数上面写下 92 | ```c++ 93 | void get_Next(char *p) { 94 | int m = strlen(p); 95 | Next[0] = Next[1] = 0; 96 | for (int i = 1; i < m; ++i) { 97 | int j = Next[i]; 98 | while (j && p[i] != p[j]) { 99 | j = Next[j]; 100 | } 101 | Next[i + 1] = p[i] ==p [j] ? j + 1 : 0; 102 | } 103 | } 104 | ``` 105 | 106 | #### 提示 107 | ```c++ 108 | #include 109 | #include 110 | using namespace std; 111 | int Next[1110]; 112 | void get_Next(char *p) { 113 | int m = strlen(p); 114 | Next[0] = Next[1] = 0; 115 | for (int i = 1; i < m; ++i) { 116 | int j = Next[i]; 117 | while (j && p[i] != p[j]) { 118 | j = Next[j]; 119 | } 120 | Next[i + 1] = p[i] ==p [j] ? j + 1 : 0; 121 | } 122 | } 123 | 124 | int main() { 125 | char s[1100], T[1100]; 126 | cin >> T >> s; 127 | 128 | return 0; 129 | } 130 | ``` 131 | 132 | 133 | #### 代码 134 | ```c++ 135 | #include 136 | #include 137 | using namespace std; 138 | int Next[1110]; 139 | void get_Next(char *p) { 140 | int m = strlen(p); 141 | Next[0] = Next[1] = 0; 142 | for (int i = 1; i < m; ++i) { 143 | int j = Next[i]; 144 | while (j && p[i] != p[j]) { 145 | j = Next[j]; 146 | } 147 | Next[i + 1] = p[i] ==p [j] ? j + 1 : 0; 148 | } 149 | } 150 | 151 | int main() { 152 | char s[1100], T[1100]; 153 | cin >> T >> s; 154 | 155 | return 0; 156 | } 157 | ``` 158 | 159 | 160 | --- 161 | ### 第四步 162 | #### 讲解 163 | 接下来根据前面的思想实现`kmp(char *T, char *s)`函数。 164 | 在`main`函数上方写下 165 | ```c++ 166 | int kmp(char *T, char *s) { 167 | int n = strlen(T), m = strlen(s); 168 | get_Next(s); 169 | int j = 0; 170 | for (int i = 0; i < n; ++i) { 171 | while (j && T[i] != s[j]) { 172 | j = Next[j]; 173 | } 174 | if (s[j] == T[i]) { 175 | ++j; 176 | } 177 | if (j == m) { 178 | return i - m + 1; 179 | } 180 | } 181 | return -1; 182 | } 183 | ``` 184 | 185 | #### 提示 186 | ```c++ 187 | #include 188 | #include 189 | using namespace std; 190 | int Next[1110]; 191 | void get_Next(char *p) { 192 | int m = strlen(p); 193 | Next[0] = Next[1] = 0; 194 | for (int i = 1; i < m; ++i) { 195 | int j = Next[i]; 196 | while (j && p[i] != p[j]) { 197 | j = Next[j]; 198 | } 199 | Next[i + 1] = p[i] ==p [j] ? j + 1 : 0; 200 | } 201 | } 202 | 203 | int kmp(char *T, char *s) { 204 | int n = strlen(T), m = strlen(s); 205 | get_Next(s); 206 | int j = 0; 207 | for (int i = 0; i < n; ++i) { 208 | while (j && T[i] != s[j]) { 209 | j = Next[j]; 210 | } 211 | if (s[j] == T[i]) { 212 | ++j; 213 | } 214 | if (j == m) { 215 | return i - m + 1; 216 | } 217 | } 218 | return -1; 219 | } 220 | int main() { 221 | char s[1100], T[1100]; 222 | cin >> T >> s; 223 | 224 | return 0; 225 | } 226 | ``` 227 | 228 | 229 | #### 代码 230 | ```c++ 231 | #include 232 | #include 233 | using namespace std; 234 | int Next[1110]; 235 | void get_Next(char *p) { 236 | int m = strlen(p); 237 | Next[0] = Next[1] = 0; 238 | for (int i = 1; i < m; ++i) { 239 | int j = Next[i]; 240 | while (j && p[i] != p[j]) { 241 | j = Next[j]; 242 | } 243 | Next[i + 1] = p[i] ==p [j] ? j + 1 : 0; 244 | } 245 | } 246 | 247 | int kmp(char *T, char *s) { 248 | int n = strlen(T), m = strlen(s); 249 | get_Next(s); 250 | int j = 0; 251 | for (int i = 0; i < n; ++i) { 252 | while (j && T[i] != s[j]) { 253 | j = Next[j]; 254 | } 255 | if (s[j] == T[i]) { 256 | ++j; 257 | } 258 | if (j == m) { 259 | return i - m + 1; 260 | } 261 | } 262 | return -1; 263 | } 264 | int main() { 265 | char s[1100], T[1100]; 266 | cin >> T >> s; 267 | 268 | return 0; 269 | } 270 | ``` 271 | 272 | 273 | --- 274 | ### 第五步 275 | #### 讲解 276 | 最后调用`kmp(T, s)`函数输出计算的结果。在`main`函数里面写下 277 | ```c++ 278 | cout << kmp(T, s) << endl; 279 | ``` 280 | 281 | #### 提示 282 | ```c++ 283 | #include 284 | #include 285 | using namespace std; 286 | int Next[1110]; 287 | void get_Next(char *p) { 288 | int m = strlen(p); 289 | Next[0] = Next[1] = 0; 290 | for (int i = 1; i < m; ++i) { 291 | int j = Next[i]; 292 | while (j && p[i] != p[j]) { 293 | j = Next[j]; 294 | } 295 | Next[i + 1] = p[i] ==p [j] ? j + 1 : 0; 296 | } 297 | } 298 | 299 | int kmp(char *T, char *s) { 300 | int n = strlen(T), m = strlen(s); 301 | get_Next(s); 302 | int j = 0; 303 | for (int i = 0; i < n; ++i) { 304 | while (j && T[i] != s[j]) { 305 | j = Next[j]; 306 | } 307 | if (s[j] == T[i]) { 308 | ++j; 309 | } 310 | if (j == m) { 311 | return i - m + 1; 312 | } 313 | } 314 | return -1; 315 | } 316 | int main() { 317 | char s[1100], T[1100]; 318 | cin >> T >> s; 319 | cout << kmp(T, s) << endl; 320 | return 0; 321 | } 322 | ``` 323 | 324 | 325 | #### 代码 326 | ```c++ 327 | #include 328 | #include 329 | using namespace std; 330 | int Next[1110]; 331 | void get_Next(char *p) { 332 | int m = strlen(p); 333 | Next[0] = Next[1] = 0; 334 | for (int i = 1; i < m; ++i) { 335 | int j = Next[i]; 336 | while (j && p[i] != p[j]) { 337 | j = Next[j]; 338 | } 339 | Next[i + 1] = p[i] ==p [j] ? j + 1 : 0; 340 | } 341 | } 342 | 343 | int kmp(char *T, char *s) { 344 | int n = strlen(T), m = strlen(s); 345 | get_Next(s); 346 | int j = 0; 347 | for (int i = 0; i < n; ++i) { 348 | while (j && T[i] != s[j]) { 349 | j = Next[j]; 350 | } 351 | if (s[j] == T[i]) { 352 | ++j; 353 | } 354 | if (j == m) { 355 | return i - m + 1; 356 | } 357 | } 358 | return -1; 359 | } 360 | int main() { 361 | char s[1100], T[1100]; 362 | cin >> T >> s; 363 | cout << kmp(T, s) << endl; 364 | return 0; 365 | } 366 | ``` 367 | 368 | 369 | 370 | --- 371 | ### 完成讲解 372 | 终于完成了,点击运行,看看效果吧。 373 | 374 | 聪明的你一定学会了如何使用`kmp算法`了。 375 | -------------------------------------------------------------------------------- /9_string_basic/2_exkmp.md: -------------------------------------------------------------------------------- 1 | # exkmp 的使用 2 | - exkmp 3 | 4 | ### 文件名 5 | exkmp.cpp 6 | 7 | ### 分数 8 | 1 9 | 10 | --- 11 | ### 初始化代码 12 | ```c++ 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | int main() { 18 | 19 | return 0; 20 | } 21 | ``` 22 | 23 | --- 24 | ### 第一步 25 | #### 讲解 26 | 这一节我们学习扩展KMP(exkmp)算法。 27 | 扩展kmp是对KMP算法的扩展,它解决如下问题: 28 | 定义母串S,和子串T,设S的长度为n,T的长度为m,求T与S的每一个后缀的最长公共前缀,也就是说,设extend数组,extend[i]表示T与S[i,n-1]的最长公共前缀,要求出所有extend[i](0<=i 39 | #include 40 | using namespace std; 41 | const int maxn=1100; 42 | int Next[maxn], extend[maxn]; 43 | 44 | int main() { 45 | 46 | return 0; 47 | } 48 | ``` 49 | 50 | --- 51 | ### 第二步 52 | #### 讲解 53 | 接下来输入字符串`s`和`T`。 54 | 在`main`函数里面写下 55 | ```c++ 56 | char s[maxn], T[maxn]; 57 | cin >> T >> s; 58 | ``` 59 | 60 | #### 提示 61 | ```c++ 62 | #include 63 | #include 64 | using namespace std; 65 | const int maxn=1100; 66 | int Next[maxn], extend[maxn]; 67 | 68 | int main() { 69 | char s[maxn], T[maxn]; 70 | cin >> T >> s; 71 | 72 | return 0; 73 | } 74 | ``` 75 | 76 | #### 代码 77 | ```c++ 78 | #include 79 | #include 80 | using namespace std; 81 | const int maxn=1100; 82 | int Next[maxn], extend[maxn]; 83 | 84 | int main() { 85 | char s[maxn], T[maxn]; 86 | cin >> T >> s; 87 | 88 | return 0; 89 | } 90 | ``` 91 | 92 | --- 93 | ### 第三步 94 | #### 讲解 95 | 然后实现`get_Next(char *s)`函数。 96 | 预处理出`Next`数组是算法的关键。 97 | 在`main`函数上方写下 98 | ```c++ 99 | void get_Next(char *s) { 100 | int n = strlen(s); 101 | int i, j, k; 102 | for(j = 0; 1 + j < n && s[j] == s[1 + j]; ++j); 103 | Next[1] = j; 104 | k = 1; 105 | for(i = 2; i < n; ++i) { 106 | int len = k + Next[k], L = Next[i - k]; 107 | if (L < len - i) { 108 | Next[i] = L; 109 | } else { 110 | for (j = max(0, len - i); i + j < n && s[j] == s[i + j]; ++j); 111 | Next[i] = j; 112 | k = i; 113 | } 114 | } 115 | Next[0] = n; 116 | } 117 | ``` 118 | 119 | #### 提示 120 | ```c++ 121 | #include 122 | #include 123 | using namespace std; 124 | const int maxn=1100; 125 | int Next[maxn], extend[maxn]; 126 | void get_Next(char *s) { 127 | int n = strlen(s); 128 | int i, j, k; 129 | for(j = 0; 1 + j < n && s[j] == s[1 + j]; ++j); 130 | Next[1] = j; 131 | k = 1; 132 | for(i = 2; i < n; ++i) { 133 | int len = k + Next[k], L = Next[i - k]; 134 | if (L < len - i) { 135 | Next[i] = L; 136 | } else { 137 | for (j = max(0, len - i); i + j < n && s[j] == s[i + j]; ++j); 138 | Next[i] = j; 139 | k = i; 140 | } 141 | } 142 | Next[0] = n; 143 | } 144 | 145 | int main() { 146 | char s[maxn], T[maxn]; 147 | cin >> T >> s; 148 | 149 | return 0; 150 | } 151 | ``` 152 | 153 | 154 | #### 代码 155 | ```c++ 156 | #include 157 | #include 158 | using namespace std; 159 | const int maxn=1100; 160 | int Next[maxn], extend[maxn]; 161 | void get_Next(char *s) { 162 | int n = strlen(s); 163 | int i, j, k; 164 | for(j = 0; 1 + j < n && s[j] == s[1 + j]; ++j); 165 | Next[1] = j; 166 | k = 1; 167 | for(i = 2; i < n; ++i) { 168 | int len = k + Next[k], L = Next[i - k]; 169 | if (L < len - i) { 170 | Next[i] = L; 171 | } else { 172 | for (j = max(0, len - i); i + j < n && s[j] == s[i + j]; ++j); 173 | Next[i] = j; 174 | k = i; 175 | } 176 | } 177 | Next[0] = n; 178 | } 179 | 180 | int main() { 181 | char s[maxn], T[maxn]; 182 | cin >> T >> s; 183 | 184 | return 0; 185 | } 186 | ``` 187 | 188 | 189 | --- 190 | ### 第四步 191 | #### 讲解 192 | 接下来根据前面的思想实现`ex_kmp(char *T, char *s)`函数,和kmp一样分情况讨论。 193 | 在`main`函数上方写下 194 | ```c++ 195 | void ex_kmp(char *T, char *s) { 196 | int n = strlen(T), m = strlen(s); 197 | int i, j, k; 198 | for(j = 0; j < n && j < m && T[j] == s[j]; ++j); 199 | extend[0] = j; 200 | k = 0; 201 | for (i = 1; i < n; ++i) { 202 | int len = k + extend[k], L = Next[i - k]; 203 | if(L < len-i) { 204 | extend[i] = L; 205 | } else { 206 | for (j = max(0, len - i); j < m && i + j < n && s[j] == T[i + j]; ++j); 207 | extend[i] = j; 208 | k = i; 209 | } 210 | } 211 | } 212 | ``` 213 | 214 | #### 提示 215 | ```c++ 216 | #include 217 | #include 218 | using namespace std; 219 | const int maxn=1100; 220 | int Next[maxn], extend[maxn]; 221 | void get_Next(char *s) { 222 | int n = strlen(s); 223 | int i, j, k; 224 | for(j = 0; 1 + j < n && s[j] == s[1 + j]; ++j); 225 | Next[1] = j; 226 | k = 1; 227 | for(i = 2; i < n; ++i) { 228 | int len = k + Next[k], L = Next[i - k]; 229 | if (L < len - i) { 230 | Next[i] = L; 231 | } else { 232 | for (j = max(0, len - i); i + j < n && s[j] == s[i + j]; ++j); 233 | Next[i] = j; 234 | k = i; 235 | } 236 | } 237 | Next[0] = n; 238 | } 239 | void ex_kmp(char *T, char *s) { 240 | int n = strlen(T), m = strlen(s); 241 | int i, j, k; 242 | for(j = 0; j < n && j < m && T[j] == s[j]; ++j); 243 | extend[0] = j; 244 | k = 0; 245 | for (i = 1; i < n; ++i) { 246 | int len = k + extend[k], L = Next[i - k]; 247 | if(L < len-i) { 248 | extend[i] = L; 249 | } else { 250 | for (j = max(0, len - i); j < m && i + j < n && s[j] == T[i + j]; ++j); 251 | extend[i] = j; 252 | k = i; 253 | } 254 | } 255 | } 256 | 257 | int main() { 258 | char s[maxn], T[maxn]; 259 | cin >> T >> s; 260 | 261 | return 0; 262 | } 263 | ``` 264 | 265 | 266 | #### 代码 267 | ```c++ 268 | #include 269 | #include 270 | using namespace std; 271 | const int maxn=1100; 272 | int Next[maxn], extend[maxn]; 273 | void get_Next(char *s) { 274 | int n = strlen(s); 275 | int i, j, k; 276 | for(j = 0; 1 + j < n && s[j] == s[1 + j]; ++j); 277 | Next[1] = j; 278 | k = 1; 279 | for(i = 2; i < n; ++i) { 280 | int len = k + Next[k], L = Next[i - k]; 281 | if (L < len - i) { 282 | Next[i] = L; 283 | } else { 284 | for (j = max(0, len - i); i + j < n && s[j] == s[i + j]; ++j); 285 | Next[i] = j; 286 | k = i; 287 | } 288 | } 289 | Next[0] = n; 290 | } 291 | void ex_kmp(char *T, char *s) { 292 | int n = strlen(T), m = strlen(s); 293 | int i, j, k; 294 | for(j = 0; j < n && j < m && T[j] == s[j]; ++j); 295 | extend[0] = j; 296 | k = 0; 297 | for (i = 1; i < n; ++i) { 298 | int len = k + extend[k], L = Next[i - k]; 299 | if(L < len-i) { 300 | extend[i] = L; 301 | } else { 302 | for (j = max(0, len - i); j < m && i + j < n && s[j] == T[i + j]; ++j); 303 | extend[i] = j; 304 | k = i; 305 | } 306 | } 307 | } 308 | 309 | int main() { 310 | char s[maxn], T[maxn]; 311 | cin >> T >> s; 312 | 313 | return 0; 314 | } 315 | ``` 316 | 317 | 318 | --- 319 | ### 第五步 320 | #### 讲解 321 | 最后输出所有的`extend[i]`即最长公共前缀。在`main`函数里面写下 322 | ```c++ 323 | for (int i = 0; i < strlen(T); ++i) { 324 | cout << extend[i] << " "; 325 | } 326 | cout << endl; 327 | ``` 328 | 329 | #### 提示 330 | ```c++ 331 | #include 332 | #include 333 | using namespace std; 334 | const int maxn=1100; 335 | int Next[maxn], extend[maxn]; 336 | void get_Next(char *s) { 337 | int n = strlen(s); 338 | int i, j, k; 339 | for(j = 0; 1 + j < n && s[j] == s[1 + j]; ++j); 340 | Next[1] = j; 341 | k = 1; 342 | for(i = 2; i < n; ++i) { 343 | int len = k + Next[k], L = Next[i - k]; 344 | if (L < len - i) { 345 | Next[i] = L; 346 | } else { 347 | for (j = max(0, len - i); i + j < n && s[j] == s[i + j]; ++j); 348 | Next[i] = j; 349 | k = i; 350 | } 351 | } 352 | Next[0] = n; 353 | } 354 | void ex_kmp(char *T, char *s) { 355 | int n = strlen(T), m = strlen(s); 356 | int i, j, k; 357 | for(j = 0; j < n && j < m && T[j] == s[j]; ++j); 358 | extend[0] = j; 359 | k = 0; 360 | for (i = 1; i < n; ++i) { 361 | int len = k + extend[k], L = Next[i - k]; 362 | if(L < len-i) { 363 | extend[i] = L; 364 | } else { 365 | for (j = max(0, len - i); j < m && i + j < n && s[j] == T[i + j]; ++j); 366 | extend[i] = j; 367 | k = i; 368 | } 369 | } 370 | } 371 | 372 | int main() { 373 | char s[maxn], T[maxn]; 374 | cin >> T >> s; 375 | for (int i = 0; i < strlen(T); ++i) { 376 | cout << extend[i] << " "; 377 | } 378 | cout << endl; 379 | return 0; 380 | } 381 | ``` 382 | 383 | 384 | #### 代码 385 | ```c++ 386 | #include 387 | #include 388 | using namespace std; 389 | const int maxn=1100; 390 | int Next[maxn], extend[maxn]; 391 | void get_Next(char *s) { 392 | int n = strlen(s); 393 | int i, j, k; 394 | for(j = 0; 1 + j < n && s[j] == s[1 + j]; ++j); 395 | Next[1] = j; 396 | k = 1; 397 | for(i = 2; i < n; ++i) { 398 | int len = k + Next[k], L = Next[i - k]; 399 | if (L < len - i) { 400 | Next[i] = L; 401 | } else { 402 | for (j = max(0, len - i); i + j < n && s[j] == s[i + j]; ++j); 403 | Next[i] = j; 404 | k = i; 405 | } 406 | } 407 | Next[0] = n; 408 | } 409 | void ex_kmp(char *T, char *s) { 410 | int n = strlen(T), m = strlen(s); 411 | int i, j, k; 412 | for(j = 0; j < n && j < m && T[j] == s[j]; ++j); 413 | extend[0] = j; 414 | k = 0; 415 | for (i = 1; i < n; ++i) { 416 | int len = k + extend[k], L = Next[i - k]; 417 | if(L < len-i) { 418 | extend[i] = L; 419 | } else { 420 | for (j = max(0, len - i); j < m && i + j < n && s[j] == T[i + j]; ++j); 421 | extend[i] = j; 422 | k = i; 423 | } 424 | } 425 | } 426 | 427 | int main() { 428 | char s[maxn], T[maxn]; 429 | cin >> T >> s; 430 | for (int i = 0; i < strlen(T); ++i) { 431 | cout << extend[i] << " "; 432 | } 433 | cout << endl; 434 | return 0; 435 | } 436 | ``` 437 | 438 | 439 | 440 | --- 441 | ### 完成讲解 442 | 终于完成了,点击运行,看看效果吧。 443 | 444 | 聪明的你一定学会了如何使用`exkmp算法`了。 445 | -------------------------------------------------------------------------------- /9_string_basic/code/1_kmp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | int Next[1110]; 5 | void get_Next(char *p) { 6 | int m = strlen(p); 7 | Next[0] = Next[1] = 0; 8 | for (int i = 1; i < m; ++i) { 9 | int j = Next[i]; 10 | while (j && p[i] != p[j]) { 11 | j = Next[j]; 12 | } 13 | Next[i + 1] = p[i] ==p [j] ? j + 1 : 0; 14 | } 15 | } 16 | 17 | int kmp(char *T, char *s) { 18 | int n = strlen(T), m = strlen(s); 19 | get_Next(s); 20 | int j = 0; 21 | for (int i = 0; i < n; ++i) { 22 | while (j && T[i] != s[j]) { 23 | j = Next[j]; 24 | } 25 | if (s[j] == T[i]) { 26 | ++j; 27 | } 28 | if (j == m) { 29 | return i - m + 1; 30 | } 31 | } 32 | return -1; 33 | } 34 | int main() { 35 | char s[1100], T[1100]; 36 | cin >> T >> s; 37 | cout << kmp(T, s) << endl; 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /9_string_basic/code/2_exkmp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | const int maxn=1100; 5 | int Next[maxn], extend[maxn]; 6 | void get_Next(char *s) { 7 | int n = strlen(s); 8 | int i, j, k; 9 | for(j = 0; 1 + j < n && s[j] == s[1 + j]; ++j); 10 | Next[1] = j; 11 | k = 1; 12 | for(i = 2; i < n; ++i) { 13 | int len = k + Next[k], L = Next[i - k]; 14 | if (L < len - i) { 15 | Next[i] = L; 16 | } else { 17 | for (j = max(0, len - i); i + j < n && s[j] == s[i + j]; ++j); 18 | Next[i] = j; 19 | k = i; 20 | } 21 | } 22 | Next[0] = n; 23 | } 24 | void ex_kmp(char *T, char *s) { 25 | int n = strlen(T), m = strlen(s); 26 | int i, j, k; 27 | for(j = 0; j < n && j < m && T[j] == s[j]; ++j); 28 | extend[0] = j; 29 | k = 0; 30 | for (i = 1; i < n; ++i) { 31 | int len = k + extend[k], L = Next[i - k]; 32 | if(L < len-i) { 33 | extend[i] = L; 34 | } else { 35 | for (j = max(0, len - i); j < m && i + j < n && s[j] == T[i + j]; ++j); 36 | extend[i] = j; 37 | k = i; 38 | } 39 | } 40 | } 41 | 42 | int main() { 43 | char s[maxn], T[maxn]; 44 | cin >> T >> s; 45 | ex_kmp(T, s); 46 | for (int i = 0; i < strlen(T); ++i) { 47 | cout << extend[i] << " "; 48 | } 49 | cout << endl; 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /9_string_basic/code/3_trie.cpp: -------------------------------------------------------------------------------- 1 | // 查新前缀单词数 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | const int maxn = 10010; 7 | struct Trie { 8 | int ch[maxn][26]; 9 | int cnt[maxn]; 10 | int num; 11 | void init() { 12 | memset(ch[0], 0, sizeof(ch[0])); 13 | cnt[0] = 0; 14 | num = 0; 15 | } 16 | int newnode() { 17 | ++num; 18 | memset(ch[num], 0, sizeof(ch[num])); 19 | cnt[num] = 0; 20 | return num; 21 | } 22 | void insert(char *s) { 23 | int u = 0; 24 | for (int i = 0; s[i]; ++i) { 25 | if (!ch[u][s[i] - 'a']) { 26 | ch[u][s[i] - 'a'] = newnode(); 27 | } 28 | u = ch[u][s[i] - 'a']; 29 | ++cnt[u]; 30 | } 31 | } 32 | int query(char *s) { 33 | int u = 0; 34 | for (int i = 0; s[i]; ++i) { 35 | if (!ch[u][s[i] - 'a']) { 36 | return 0; 37 | } 38 | u = ch[u][s[i] - 'a']; 39 | } 40 | return cnt[u]; 41 | } 42 | } trie; 43 | 44 | int main() { 45 | trie.init(); 46 | trie.insert("china"); 47 | trie.insert("chinese"); 48 | trie.insert("children"); 49 | trie.insert("check"); 50 | cout << trie.query("ch") << endl; 51 | cout << trie.query("chi") << endl; 52 | cout << trie.query("chin") << endl; 53 | cout << trie.query("china") << endl; 54 | cout << trie.query("beijing") << endl; 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /9_string_basic/code/4_ac.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | const int MAX_N = 10000; 5 | const int MAX_C = 26; 6 | struct AC_Automaton { 7 | int ch[MAX_N][MAX_C], fail[MAX_N], cnt[MAX_N]; 8 | int tot; 9 | void init() { 10 | memset(ch, -1, sizeof(ch)); 11 | memset(fail, 0, sizeof(fail)); 12 | tot = 0; 13 | memset(cnt, 0, sizeof(cnt)); 14 | } 15 | void insert(char* str) { 16 | int p = 0; 17 | for (int i = 0; str[i]; ++i) { 18 | if (ch[p][str[i] - 'a'] == -1) { 19 | ch[p][str[i] - 'a'] = ++tot; 20 | } 21 | p = ch[p][str[i] - 'a']; 22 | } 23 | cnt[p]++; 24 | } 25 | void build() { 26 | int l = 0, r = 0, Q[MAX_N]; 27 | for (int i = 0; i < MAX_C; i++) { 28 | if (ch[0][i] == -1) { 29 | ch[0][i] = 0; 30 | } else { 31 | Q[r++] = ch[0][i]; 32 | } 33 | } 34 | while (l < r) { 35 | int p = Q[l++]; 36 | for (int i = 0; i < MAX_C; i++) { 37 | if (ch[p][i] == -1) { 38 | ch[p][i] = ch[fail[p]][i]; 39 | } else { 40 | fail[ch[p][i]] = ch[fail[p]][i]; 41 | Q[r++] = ch[p][i]; 42 | } 43 | } 44 | } 45 | } 46 | int count(char* str) { 47 | int ret = 0, p = 0; 48 | for (int i = 0; str[i]; ++i) { 49 | p = ch[p][str[i] - 'a']; 50 | int tmp = p; 51 | while (tmp) { 52 | ret += cnt[tmp]; 53 | cnt[tmp] = 0; 54 | tmp = fail[tmp]; 55 | } 56 | } 57 | return ret; 58 | } 59 | } ac; 60 | 61 | int main() { 62 | ac.init(); 63 | ac.insert("abcd"); 64 | ac.insert("bcd"); 65 | ac.insert("cd"); 66 | ac.insert("d"); 67 | ac.build(); 68 | cout << ac.count("abcd") << endl; 69 | return 0; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /9_string_basic/content.md: -------------------------------------------------------------------------------- 1 | # 字符串模板 2 | - 乱序 3 | 4 | |类型|标题|分数|标签| 5 | |:---:|:---:|:---:|:---:| 6 | |跟随|KMP 模板|1|| 7 | |跟随|扩展KMP 模板|1|| 8 | |跟随|Trie 树模板|1|| 9 | |跟随|AC 自动机模板|1|| 10 | 11 | 图论基础。 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `算法竞赛复习全书` 2 | ``` 3 | 序:蒟蒻退役狗一只,戒游戏之后闲的无聊,遂决定重新找回遗失的青春. 4 | 夏洛特烦恼,就此... 5 | ``` 6 | 7 | #### 记录关于各类竞赛的点点滴滴 8 | - NOIP/NOI 9 | - ACM/ICPC 10 | - 蓝桥杯 11 | - CCF CSP 12 | - 其他在线比赛 13 | 14 | > 作为一个从弱校/小地方出身的伪竞赛选手,能够深深地体会到弱者为何而战的无奈与辛酸。依稀记得当初满怀梦想的踏上了竞赛的旅程,却终究抵不过现实的残酷。没有系统的训练与指导,盲目的在OJ上水题,最终一切成空。开局几本书,紫书、白书、黑书、挑战、算导,看过之后也不知懂或没懂,对于一个从小自诩为智商型的选手,这显然是不能够接受的,遂功利性的选择成为业余选手。 15 | 16 | > 作为一个过来人,说句实话,不论中学还是大学,如果你的周围没有厉害的学长或专业的教练指导那么不建议你功利性的搞算法竞赛。那些所谓自学的神犇肯定是站在了巨人的肩膀上,而你能不能找到巨人肩膀在哪,还得取决于运气。 17 | 18 | > 本书希望能帮助那些有梦想却无资源的同学,劝他们早早地放弃,亦或更加努力的爬坑。 19 | 20 | 21 | 最后,希望觉得本书写的不错的同学,推荐给周围需要的朋友,予人玫瑰,手留余香。 22 | --------------------------------------------------------------------------------