递归实现组合型枚举
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。n>0n \gt 0n>0, 0≤m≤n0 \leq m \leq n0≤m≤n, n+(n−m)≤25n+(n-m)\leq 25n+(n−m)≤25。
输入描述:
两个整数n,m。
输出描述:
按照从小到大的顺序输出所有方案,每行1个。
首先,同一行内的数升序排列,相邻两个数用一个空格隔开。其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 9 12排在1 3 10 11前面)。
示例1
输入
复制5 3
5 3
输出
复制1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
#include <bits/stdc++.h> // 引入常用的库,这是C++编程中一个非官方的但广泛使用的头文件,包含了大部分标准库。
using namespace std; // 使用 std 命名空间,这样可以不用频繁地写 std:: 前缀。
vector<vector<int>> ret; // 用来存储所有组合的二维向量。
vector<int> path; // 用来存储当前正在探索的组合路径。
int n, m; // n 是可选择的最大数字,m 是组合中数字的数量。
void dfs(int pos) { // 定义一个深度优先搜索函数,参数 pos 表示当前探索的起始位置。
path.push_back(pos); // 将当前位置添加到路径中。
if (path.size() == m) { // 如果路径长度等于 m,说明找到了一个有效的组合。
ret.push_back(path); // 将当前路径(一个有效组合)添加到结果集中。
}
for (int i = pos + 1; i <= n; i++) { // 遍历所有可能的下一个位置。
dfs(i); // 递归地进行深度优先搜索。
}
path.pop_back(); // 回溯,移除路径中的最后一个元素,以探索其他可能的路径。
}
int main() { // 主函数。
int _n, _m; // 用于输入的 n 和 m 值。
cin >> _n >> _m; // 从标准输入读取 n 和 m 的值。
n = _n; // 将读入的值赋给全局变量 n。
m = _m; // 将读入的值赋给全局变量 m。
for (int i = 1; i <= n; i++) { // 从 1 到 n 遍历所有可能的起始位置。
dfs(i); // 对每个起始位置调用深度优先搜索函数。
}
for (auto x : ret) { // 遍历所有找到的组合。
for (auto y : x) { // 遍历组合中的每个数字。
cout << y << " "; // 输出数字并在数字间加空格。
}
cout << endl; // 每个组合后输出换行符。
}
}
N皇后问题
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给出一个n×nn\times nn×n的国际象棋棋盘,你需要在棋盘中摆放nnn个皇后,使得任意两个皇后之间不能互相攻击。具体来说,不能存在两个皇后位于同一行、同一列,或者同一对角线。请问共有多少种摆放方式满足条件。
输入描述:
一行,一个整数n(1≤n≤12)n(1\le n \le 12)n(1≤n≤12),表示棋盘的大小。
输出描述:
输出一行一个整数,表示总共有多少种摆放皇后的方案,使得它们两两不能互相攻击。
示例1
输入
复制4
4
输出
复制2
2
维护和回溯都在上一个节点实现
#include <bits/stdc++.h>
using namespace std;
int ret;
int n;
vector<bool> check1;
vector<bool> check2;
vector<bool> checkcol;
void dfs(int i, int j) {
int newrow = i + 1;
if (newrow == n) {
ret++;
return;
}
for (int col = 0; col < n; col++) {
if (!check1[newrow - col + n] && !check2[newrow + col] && !checkcol[col]) {
//维护和递归都在上一个节点实现
check1[newrow - col + n] = check2[newrow + col] = checkcol[col] = true;
dfs(newrow, col);
check1[newrow - col + n] = check2[newrow + col] = checkcol[col] = false;
}
}
}
int main() {
cin >> n;
check1.resize(2 * n);
check2.resize(2 * n);
checkcol.resize(n);
for (int col = 0; col < n; col++) {
维护和递归都在上一个节点实现
check1[0 - col + n] = check2[0 + col] = checkcol[col] = true;
dfs(0, col);
check1[0 - col + n] = check2[0 + col] = checkcol[col] = false;
}
cout << ret;
}
维护和回溯都在当前节点实现
#include <bits/stdc++.h>
using namespace std;
int ret;
int n;
vector<bool> check1;
vector<bool> check2;
vector<bool> checkcol;
void dfs(int i, int j) {
int newrow = i + 1;
if (newrow == n) {
ret++;
return;
}
//维护和递归都在当前节点实现
int col = j; int row = i;
check1[row - col + n] = check2[row + col] = checkcol[col] = true;
for (int newcol = 0; newcol < n; newcol++) {
if (!check1[newrow - newcol + n] && !check2[newrow + newcol] && !checkcol[newcol]) {
dfs(newrow, newcol);
}
}
check1[row - col + n] = check2[row + col] = checkcol[col] = false;
}
int main() {
cin >> n;
check1.resize(2 * n);
check2.resize(2 * n);
checkcol.resize(n);
for (int col = 0; col < n; col++) {
//维护和递归都在当前节点实现
dfs(0, col);
}
cout << ret;
}
维护在当前节点实现和回溯都在上一个节点实现
#include <bits/stdc++.h>
using namespace std;
int ret;
int n;
vector<bool> check1;
vector<bool> check2;
vector<bool> checkcol;
void dfs(int i, int j) {
int newrow = i + 1;
if (newrow == n) {
ret++;
return;
}
//维护在当前节点实现,回溯在上一节点实现
int col = j; int row = i;
check1[row - col + n] = check2[row + col] = checkcol[col] = true;
for (int newcol = 0; newcol < n; newcol++) {
if (!check1[newrow - newcol + n] && !check2[newrow + newcol] && !checkcol[newcol]) {
dfs(newrow, newcol);
check1[newrow - newcol + n] = check2[newrow + newcol] = checkcol[newcol] = false;
}
}
}
int main() {
cin >> n;
check1.resize(2 * n);
check2.resize(2 * n);
checkcol.resize(n);
for (int col = 0; col < n; col++) {
//维护在当前节点实现,回溯在上一节点实现
dfs(0, col);
check1[0 - col + n] = check2[0 + col] = checkcol[col] = false;
}
cout << ret;
}
维护在上一个节点实现和回溯都在当前节点实现
#include <bits/stdc++.h>
using namespace std;
int ret;
int n;
vector<bool> check1;
vector<bool> check2;
vector<bool> checkcol;
void dfs(int i, int j) {
int newrow = i + 1;
int col = j; int row = i;
if (newrow == n) {
ret++;
check1[row - col + n] = check2[row + col] = checkcol[col] = false;
return;
}
//维护在上一个节点实现,回溯在当前节点实现
for (int newcol = 0; newcol < n; newcol++) {
if (!check1[newrow - newcol + n] && !check2[newrow + newcol] && !checkcol[newcol]) {
check1[newrow - newcol + n] = check2[newrow + newcol] = checkcol[newcol] = true;
dfs(newrow, newcol);
}
}
check1[row - col + n] = check2[row + col] = checkcol[col] = false;
}
int main() {
cin >> n;
check1.resize(2 * n);
check2.resize(2 * n);
checkcol.resize(n);
for (int col = 0; col < n; col++) {
//维护在上一个节点实现,回溯在当前节点实现
check1[0 - col + n] = check2[0 + col] = checkcol[col] = true;
dfs(0, col);
}
cout << ret;
}
开始节点是虚拟节点
#include <bits/stdc++.h> // 引入常用的库,包括大部分标准库的头文件。
using namespace std; // 使用命名空间 std,避免在调用标准库功能时需要加前缀 std::。
int ret; // 用来存储皇后问题的解决方案数量。
int n; // 棋盘的大小,也是皇后的数量。
vector<bool> check1; // 用来标记哪些主对角线上已被占用,防止皇后相互攻击。
vector<bool> check2; // 用来标记哪些副对角线上已被占用,防止皇后相互攻击。
vector<bool> checkcol; // 用来标记哪些列已被占用,防止皇后相互攻击。
void dfs(int row){ // 深度优先搜索函数,参数 row 表示当前正在处理的行。
if(row == n) { // 如果已经处理完所有行,意味着找到了一个解决方案。
ret++; // 解决方案数加一。
return; // 返回上一层。
}
for(int col = 0; col < n; col++){ // 遍历当前行的每一列。
if(!check1[row - col + n] && !check2[row + col] && !checkcol[col]){ // 检查当前位置的列、主对角线和副对角线是否未被占用。
check1[row - col + n] = check2[row + col] = checkcol[col] = true; // 标记当前列和两个对角线为占用状态。
dfs(row + 1); // 处理下一行。
check1[row - col + n] = check2[row + col] = checkcol[col] = false; // 回溯,取消当前位置的标记,尝试下一个可能的列。
}
}
}
int main(){ // 主函数。
cin >> n; // 从标准输入读取棋盘大小。
check1.resize(2 * n); // 调整数组大小,保证主对角线的索引不会出现负数。
check2.resize(2 * n); // 调整数组大小,适应所有可能的副对角线。
checkcol.resize(n); // 调整列检查数组的大小。
dfs(0); // 从第0行开始深度优先搜索。
cout << ret; // 输出找到的解决方案总数。
}
中序序列
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给定一棵有n个结点的二叉树的先序遍历与后序遍历序列,求其中序遍历序列。
若某节点只有一个子结点,则此处将其看作左儿子结点
示例1
输入
复制5,[3,2,1,4,5],[1,5,4,2,3]
5,[3,2,1,4,5],[1,5,4,2,3]
返回值
复制[1,2,5,4,3]
[1,2,5,4,3]
说明
![]()
备注:
1≤n≤100,0001 \leq n \leq 100,0001≤n≤100,000
#include<bits/stdc++.h> // 引入常用的库,包括大部分标准库的头文件。
using namespace std; // 使用命名空间 std,避免在调用标准库功能时需要加前缀 std::。
class Solution { // 定义一个名为 Solution 的类。
public:
struct TreeNode { // 定义一个结构体TreeNode,用来表示二叉树的节点。
int val; // 节点存储的值。
TreeNode* left; // 指向左子树的指针。
TreeNode* right; // 指向右子树的指针。
};
vector<int> pre, suf; // 分别用来存储前序遍历和后序遍历的序列。
unordered_map<int, int> hash1; // 用来存储后序遍历中每个值对应的索引。
// 递归函数用于根据前序和后序遍历重建二叉树。
TreeNode* dfs(int pre_start, int pre_end, int suf_start, int suf_end) {
if (pre_start == pre_end) { // 如果当前只有一个节点。
TreeNode* root = new TreeNode; // 创建新节点。
root->val = pre[pre_start]; // 设置节点值。
root->left = root->right = nullptr; // 设置左右子节点为空。
return root;
}
if (pre_start > pre_end) return nullptr; // 如果序列无效,则返回空。
TreeNode* root = new TreeNode; // 创建根节点。
root->val = pre[pre_start]; // 设置根节点的值。
int leftroot = pre[pre_start + 1]; // 获取左子树的根节点值。
int index = hash1[leftroot]; // 找到左子树根节点值在后序遍历中的位置。
int left_length = index - suf_start + 1; // 计算左子树的长度。
// 递归构建左右子树。
root->left = dfs(pre_start + 1, pre_start + left_length, suf_start, index);
root->right = dfs(pre_start + left_length + 1, pre_end, index + 1, suf_end);
return root;
}
vector<int> ret; // 用来存储中序遍历结果。
// 主函数,接受节点数量、前序和后序序列,返回中序遍历序列。
vector<int> solve(int n, vector<int>& _pre, vector<int>& _suf) {
pre = _pre, suf = _suf; // 赋值给成员变量。
for (int i = 0; i < suf.size(); i++) { // 构建后序遍历的索引哈希表。
hash1[suf[i]] = i;
}
TreeNode* root = dfs(0, pre.size() - 1, 0, suf.size() - 1); // 从序列中重建二叉树。
dfs1(root); // 进行中序遍历。
return ret; // 返回中序遍历结果。
}
// 辅助递归函数,用于中序遍历二叉树并存储结果。
void dfs1(TreeNode* root) {
if (root == nullptr) return; // 如果节点为空,则返回。
dfs1(root->left); // 遍历左子树。
ret.push_back(root->val); // 访问当前节点。
dfs1(root->right); // 遍历右子树。
}
};
[NOIP2001]求先序排列
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度 ≤ 8)。
输入描述:
2行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。
输出描述:
1行,表示一棵二叉树的先序。
示例1
输入
复制BADC BDCA
BADC
BDCA
输出
复制ABCD
ABCD
#include <bits/stdc++.h> // 引入常用的库,包括大部分标准库的头文件。
using namespace std; // 使用命名空间 std,避免在调用标准库功能时需要加前缀 std::。
class TreeNode { // 定义一个名为 TreeNode 的类,用于表示二叉树的节点。
public:
char val; // 节点存储的值,此处为字符类型。
TreeNode* left; // 指向左子节点的指针。
TreeNode* right; // 指向右子节点的指针。
};
string midorder; // 存储中序遍历的字符串。
string backorder; // 存储后序遍历的字符串。
unordered_map<char, int> hash1; // 用于存储中序遍历中字符与其索引的映射,便于快速查找。
int n, m; // n 是中序遍历字符串的长度,m 是后序遍历字符串的长度。
// dfs 函数用于根据中序和后序遍历重建二叉树的一部分,并返回这部分的根节点。
TreeNode* dfs(int left, int right) { // 参数left和right定义了当前处理的中序遍历的区间。
TreeNode* root; // 声明一个指向TreeNode的指针,用于存储当前子树的根节点。
if (left == right) { // 如果当前区间只有一个元素。
root = new TreeNode; // 创建新节点。
root->val = midorder[left]; // 设置节点值。
root->left = root->right = nullptr; // 设置左右子节点为空。
return root; // 返回当前节点。
}
if (left > right) { // 如果区间无效,返回空指针。
return nullptr;
}
for (int i = m - 1; i >= 0; i--) { // 从后序遍历的最后一个元素向前遍历。
int pos = hash1[backorder[i]]; // 获取后序遍历当前元素在中序遍历中的索引。
if (pos >= left && pos <= right) { // 检查当前元素是否在[left, right]区间内。
root = new TreeNode; // 创建新的根节点。
root->val = midorder[pos]; // 设置根节点的值。
root->left = dfs(left, pos - 1); // 递归构建左子树。
root->right = dfs(pos + 1, right); // 递归构建右子树。
break; // 找到根节点后跳出循环。
}
}
return root; // 返回构建的树的根节点。
}
// dfs1 函数用于前序遍历二叉树并输出。
void dfs1(TreeNode* root) {
if (root == nullptr) return; // 如果节点为空,返回。
cout << root->val; // 输出当前节点的值。
dfs1(root->left); // 递归前序遍历左子树。
dfs1(root->right); // 递归前序遍历右子树。
}
int main() {
cin >> midorder >> backorder; // 读取中序和后序遍历的字符串。
n = midorder.size(); // 获取中序遍历字符串的长度。
m = backorder.size(); // 获取后序遍历字符串的长度。
for (int i = 0; i < n; i++) { // 遍历中序遍历字符串。
hash1[midorder[i]] = i; // 存储每个字符及其索引到hash表中。
}
TreeNode* root = dfs(0, n - 1); // 从整个区间重建二叉树。
dfs1(root); // 前序遍历重建的二叉树并输出。
}
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!