题解:学而思编程 安全密码

【题目来源】

学而思编程:安全密码

【题目描述】

小明想要设置一个由数字 \(3\)\(6\)\(9\) 组成的 \(n\) 位密码。为了安全起见,密码中不允许出现以下三种连续的两个组合:

  • \(36\)(即 \(3\) 后面不能紧跟着 \(6\)
  • \(69\)(即 \(6\) 后面不能紧跟着 \(9\)
  • \(93\)(即 \(9\) 后面不能紧跟着 \(3\)

满足这个条件的密码称为“安全密码”。

现在小明想知道,在所有 \(n\) 位的安全密码中,按字典序从小到大排列,第 \(k\) 个密码是什么?

【输入】

一行,两个正整数 \(n,k\),用空格分隔。

【输出】

一行,输出第 \(k\) 个安全密码。

【输入样例】

2 3

【输出样例】

63

【核心思想】

  1. 问题分析:给定密码长度 \(n\),密码只能由数字 \(3\)\(6\)\(9\) 组成,且不能出现三种禁止的连续组合(\(36\)\(69\)\(93\))。需要按字典序从小到大找出第 \(k\) 个安全密码。这是一个DFS枚举 + 剪枝问题,关键在于按字典序生成所有合法密码并计数。

  2. 算法选择

    • 深度优先搜索(DFS):按位枚举密码的每一位
    • 字典序生成:按 \(3 \to 6 \to 9\) 的顺序尝试,天然满足字典序
    • 剪枝策略:检查当前位与前一位是否构成禁止组合,提前跳过非法分支
  3. 关键步骤

    • 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)\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(3^n)\),最坏情况下需要枚举所有 \(3^n\) 种密码组合
    • 空间复杂度:\(O(n)\),递归栈深度为 \(n\),存储当前密码的数组
  5. 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
posted @ 2026-06-17 09:28  团爸讲算法  阅读(14)  评论(0)    收藏  举报