主页 > 开源代码  > 

【一个月备战蓝桥算法】递归与递推

【一个月备战蓝桥算法】递归与递推
字典序

在刷题和计算机科学领域,字典序(Lexicographical order)也称为词典序、字典顺序、字母序,是一种对序列元素进行排序的方式,它模仿了字典中单词的排序规则。下面从不同的数据类型来详细解释字典序:

字符串的字典序

在字典中,单词是按照字母的先后顺序排列的。对于两个字符串,字典序的比较规则如下:

比较过程:从两个字符串的第一个字符开始逐个比较,如果对应位置的字符不同,则字符 ASCII 码值小的字符串排在前面;如果对应位置字符相同,则继续比较下一个位置的字符,直到出现不同字符或者其中一个字符串结束。

示例:

比较 "apple" 和 "banana",因为第一个字符 'a' 的 ASCII 码值小于 'b',所以 "apple" 在字典序中排在 "banana" 前面。

比较 "apple" 和 "app",前三个字符都相同,但 "app" 先结束,所以 "app" 在字典序中排在 "apple" 前面。

整数序列的字典序

对于整数序列,同样可以按照字典序进行比较:

比较过程:将整数序列看作由数字组成的字符串,从序列的第一个元素开始逐个比较元素的大小,如果对应位置的元素不同,则元素值小的序列排在前面;如果对应位置元素相同,则继续比较下一个位置的元素,直到出现不同元素或者其中一个序列结束。

示例:

比较序列 [1, 2, 3] 和 [2, 1, 3],第一个元素 1 小于 2,所以 [1, 2, 3] 在字典序中排在 [2, 1, 3] 前面。

比较序列 [1, 2, 3] 和 [1, 2],前两个元素都相同,但 [1, 2] 先结束,所以 [1, 2] 在字典序中排在 [1, 2, 3] 前面。

在刷题中的应用

在很多算法题中,字典序常常作为排序的依据或者要求输出的结果满足字典序的要求,例如:

全排列问题:要求输出给定序列的所有全排列,并且按照字典序输出。例如,对于序列 [1, 2, 3],其全排列按照字典序输出为 [1, 2, 3]、[1, 3, 2]、[2, 1, 3]、[2, 3, 1]、[3, 1, 2]、[3, 2, 1]。

子集问题:可能要求输出所有子集,并且按照字典序排列。

代码示例(C++ 实现全排列并按字典序输出)

#include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> nums = {1, 2, 3}; do { for (int num : nums) { std::cout << num << " "; } std::cout << std::endl; } while (std::next_permutation(nums.begin(), nums.end())); return 0; }

这些代码示例展示了如何生成全排列并按字典序输出,在刷题中可以根据具体需求对代码进行调整。

92.递归实现指数型枚举

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 16; // 最大数据范围 int statu[N]; // 状态数组 0表示未考虑 1表示选 2表示不选 int n; // 标准输入 void dfs(int u) // d { if(u > n) // 考虑到了最后一个位置 -- 递归出口 { // 打印所有的数 for(int i = 0; i <= n; i++) { if(statu[i] == 1) printf("%d ", i); } printf("\n");// 打印换行,表示这一次枚举完毕 return;// 返回上一层 } // 不选的情况 statu[u] = 2; dfs(u+1); statu[u] = 0;// 恢复现场 // 选的情况 statu[u] = 1; dfs(u+1); statu[u] = 0; } int main() { cin >> n; dfs(1); //对第1个数进行考虑 return 0; }  94.递归实现排列型枚举

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; // 数组定义成全局变量,初始值一定是0,如果定义成局部变量,初始值随机 const int N = 10; int status[N]; // 0表示未填入 1—n表示填入的数 bool used[N];// 标记这个数有没有被用过 true用过 false没有用过 int n; void dfs(int u) { // 递归出口 if(u > n) { for(int i = 1; i <= n; i++) printf("%d ", status[i]); puts(""); return; } // 依此枚举每个分支,即当前位置可以填哪些数 for(int i = 1; i <= n; i++) { if(!used[i]) // 当前的数没有用 { status[u] = i; // 填入这个数 used[i] = true; // 标记已使用 dfs(u + 1); // 恢复现场 used[i] = false; status[u] = 0; } } } int main() { scanf("%d", &n); dfs(1); return 0; }  93.递归实现组合型枚举

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n, m; const int N = 30; int status[N]; void dfs(int u, int start) { // (u-1 + n - start + 1 < m) if(u + n - start < m) return; // 剪枝 -- start后面的数加起来都不够凑m个数 // 递归出口 if(u > m) { for(int i = 1; i <= m; i++) printf("%d ", status[i]); puts(""); return; } for(int i = start; i <= n; i++) { status[u] = i; dfs(u+1, i+1); // 恢复现场 status[u] = 0; } } int main() { scanf("%d %d", &n, &m); dfs(1, 1); return 0; }  1209.带分数

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 10; int ans = 0; int n; bool status[N]; // 判重数组 bool backup[N]; bool check(int a, int c) { long long b = n * (long long)c - a * c; // a b c 不能为0 if(!a || !b || !c) return false; memcpy(backup, status, sizeof(status)); while(b) { int x = b % 10; b = b / 10; // x在ac中不能出现, x不能为0 if(!x || backup[x]) return false; backup[x] = true; } // 看看每个数字是否出现过 -- 必须全部出现 for(int i = 1; i <= 9; i++) { if(!backup[i]) return false; } return true; } void dfs_c(int u, int a, int c) { if(u == n) return; if(check(a, c)) ans++; for(int i = 1; i <= 9; i++) { if(!status[i]) { status[i] = true; dfs_c(u+1, a, c * 10 +i); status[i] = false; } } } void dfs_a(int u, int a) { if(a >= n) return; if(a) dfs_c(u, a, 0); // 只要a小于n,每种情况下都有dfs_c for(int i = 1; i <= 9; i++) { if(!status[i]) { status[i] = true; dfs_a(u+1, a * 10 + i); status[i] = false; } } } int main() { cin >> n; dfs_a(0, 0); // 当前已经用了多少个数字, 最开始a是0 cout << ans << endl; return 0; }  717.简单斐波那契

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int main() { int n = 0; cin >> n; int F[47]; F[0] = 0, F[1] = 1, F[2] = 1; for(int i = 3; i <= n; i++) { F[i] = F[i-1] + F[i-2]; } for(int i = 0; i < n; i++) { cout << F[i] << " "; } return 0; }

 优化

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int main() { int n; cin >> n; int a = 0, b = 1; for(int i = 1; i <= n; i++) { cout << a << ' '; int fn = a + b; a = b; b = fn; } return 0; } 1208.翻硬币

#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> using namespace std; const int N = 110; char start[N], aim[N]; void turn(int i) { if(start[i] == '*') start[i] = 'o'; else start[i] = '*'; } int main() { cin >> start >> aim; int n = strlen(start);// 计算输入长度 int ret = 0; for(int i = 0; i < n - 1; i++) { if(start[i] != aim[i]) { turn(i), turn(i+1); ret++; } } cout << ret << endl; return 0; }  116.飞行员兄弟

#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> using namespace std; const int N = 5; char g[N][N], backup[N][N]; typedef pair<int, int> PII; // 映射函数 int get(int i, int j) { return i * 4 + j; } void turn_one(int x, int y) { if(g[x][y] == '-') g[x][y] = '+'; else g[x][y] = '-'; } void turn_all(int x, int y) { for(int i = 0; i < 4; i++) { turn_one(x, i); turn_one(i, y); } turn_one(x, y); // xy在循环中被按了两次,现在调回去 } int main() { vector<PII> res; // 输入 for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++) cin >> g[i][j]; // 枚举所有方案 for(int op = 0; op < (1 << 16); op++) { vector<PII> temp; // 存储方案 memcpy(backup, g, sizeof(g)); // 备份方案 // 枚举16个位置 for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++) { if(op >> get(i, j) &1) // 判断是不是要按开关 { temp.push_back({i, j}); turn_all(i, j); } } bool hash_close = false; // 判断是否全部灯泡已经亮了 for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++) if(g[i][j] == '+') hash_close = true; if(!hash_close) { if(res.empty() || res.size() > temp.size() ) res = temp; } memcpy(g, backup, sizeof(backup)); // 恢复方案 } cout << res.size() << endl; for (auto op : res) cout << op.first + 1 << ' ' << op.second + 1 << endl; return 0; }

标签:

【一个月备战蓝桥算法】递归与递推由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【一个月备战蓝桥算法】递归与递推