题解:学而思编程 安全密码
【题目来源】
学而思编程:安全密码
【题目描述】
小明想要设置一个由数字 \(3\)、\(6\)、\(9\) 组成的 \(n\) 位密码。为了安全起见,密码中不允许出现以下三种连续的两个组合:
- \(36\)(即 \(3\) 后面不能紧跟着 \(6\))
- \(69\)(即 \(6\) 后面不能紧跟着 \(9\))
- \(93\)(即 \(9\) 后面不能紧跟着 \(3\))
满足这个条件的密码称为“安全密码”。
现在小明想知道,在所有 \(n\) 位的安全密码中,按字典序从小到大排列,第 \(k\) 个密码是什么?
【输入】
一行,两个正整数 \(n,k\),用空格分隔。
【输出】
一行,输出第 \(k\) 个安全密码。
【输入样例】
2 3
【输出样例】
63
【核心思想】
-
问题分析:给定密码长度 \(n\),密码只能由数字 \(3\)、\(6\)、\(9\) 组成,且不能出现三种禁止的连续组合(\(36\)、\(69\)、\(93\))。需要按字典序从小到大找出第 \(k\) 个安全密码。这是一个DFS枚举 + 剪枝问题,关键在于按字典序生成所有合法密码并计数。
-
算法选择:
- 深度优先搜索(DFS):按位枚举密码的每一位
- 字典序生成:按 \(3 \to 6 \to 9\) 的顺序尝试,天然满足字典序
- 剪枝策略:检查当前位与前一位是否构成禁止组合,提前跳过非法分支
-
关键步骤:
-
Step 1 - 初始化:
- 读取 \(n\)(密码位数)和 \(k\)(目标序号)
- 初始化计数器 \(cnt = 0\)
-
Step 2 - DFS枚举:
- 参数 \(step\) 表示当前填充第几位
- 终止条件:当 \(step > n\) 时,表示填满 \(n\) 位,\(cnt\) 加 \(1\)
- 若 \(cnt = k\),输出当前密码并结束
-
Step 3 - 按字典序尝试数字:
- 按 \(3\)、\(6\)、\(9\) 的顺序尝试(保证字典序)
- 将当前数字填入 \(p[step]\)
-
Step 4 - 剪枝检查:
- 检查当前位 \(p[step]\) 与前一位 \(p[step-1]\) 是否构成禁止组合
- 禁止组合:\((3,6)\)、\((6,9)\)、\((9,3)\)
- 若构成禁止组合,跳过该分支(
continue)
-
Step 5 - 递归搜索:
- 若通过剪枝检查,递归调用 \(dfs(step + 1)\)
-
-
时间/空间复杂度:
- 时间复杂度:\(O(3^n)\),最坏情况下需要枚举所有 \(3^n\) 种密码组合
- 空间复杂度:\(O(n)\),递归栈深度为 \(n\),存储当前密码的数组
-
DFS枚举与剪枝的核心思想:
- 字典序生成:按固定顺序(\(3 \to 6 \to 9\))尝试数字,确保生成的密码天然按字典序排列
- 剪枝优化:在DFS过程中提前检查约束条件,跳过非法分支,避免无效搜索
- 计数器技巧:使用全局计数器 \(cnt\) 记录已生成的合法密码数量,当达到 \(k\) 时输出答案
- 状态表示:使用数组 \(p[]\) 存储当前生成的密码,便于回溯和输出
- 适用于组合枚举、排列生成、约束满足类问题
【算法标签】
DFS-一维
【代码详解】
#include <bits/stdc++.h>
using namespace std;
// ================= 常量与全局变量 =================
const int N = 25; // 最大位数
int n; // 需要生成的数字位数
int k; // 需要输出的第 k 个排列
int cnt; // 当前已生成的排列计数器
int p[N]; // 存储当前生成的排列
// ================= 深度优先搜索 =================
void dfs(int step)
{
// 如果已经填满了 n 位
if (step > n)
{
cnt++; // 排列计数加 1
// 如果当前排列是第 k 个排列
if (cnt == k)
{
// 输出该排列
for (int i = 1; i <= n; i++)
cout << p[i];
cout << endl;
}
return;
}
// 当前位置只能选择数字 3、6、9
for (int i = 3; i <= 9; i += 3)
{
p[step] = i; // 将当前数字填入第 step 位
// 禁止出现连续的三的倍数递增序列:3→6, 6→9, 9→3
if (p[step - 1] == 3 && p[step] == 6) continue;
if (p[step - 1] == 6 && p[step] == 9) continue;
if (p[step - 1] == 9 && p[step] == 3) continue;
// 递归处理下一位
dfs(step + 1);
}
}
// ================= 主函数 =================
int main()
{
// 读取位数 n 和目标序号 k
cin >> n >> k;
// 从第一位开始深度优先搜索
dfs(1);
return 0;
}
【运行结果】
2 3
63
浙公网安备 33010602011771号