【数学】【深度优先搜索】【图论】【欧拉环路】753. 破解保险箱

作者推荐

动态规划的时间复杂度优化

本文涉及知识点

数学 深度优先搜索 图论 欧拉环路

LeetCode753. 破解保险箱

有一个需要密码才能打开的保险箱。密码是 n 位数, 密码的每一位都是范围 [0, k - 1] 中的一个数字。
保险箱有一种特殊的密码校验方法,你可以随意输入密码序列,保险箱会自动记住 最后 n 位输入 ,如果匹配,则能够打开保险箱。
例如,正确的密码是 “345” ,并且你输入的是 “012345” :
输入 0 之后,最后 3 位输入是 “0” ,不正确。
输入 1 之后,最后 3 位输入是 “01” ,不正确。
输入 2 之后,最后 3 位输入是 “012” ,不正确。
输入 3 之后,最后 3 位输入是 “123” ,不正确。
输入 4 之后,最后 3 位输入是 “234” ,不正确。
输入 5 之后,最后 3 位输入是 “345” ,正确,打开保险箱。
在只知道密码位数 n 和范围边界 k 的前提下,请你找出并返回确保在输入的 某个时刻 能够打开保险箱的任一 最短 密码序列 。
示例 1:
输入:n = 1, k = 2
输出:“10”
解释:密码只有 1 位,所以输入每一位就可以。“01” 也能够确保打开保险箱。
示例 2:
输入:n = 2, k = 2
输出:“01100”
解释:对于每种可能的密码:

  • “00” 从第 4 位开始输入。
  • “01” 从第 1 位开始输入。
  • “10” 从第 3 位开始输入。
  • “11” 从第 2 位开始输入。
    因此 “01100” 可以确保打开保险箱。“01100”、“10011” 和 “11001” 也可以确保打开保险箱。

提示:
1 <= n <= 4
1 <= k <= 10
1 <= kn <= 4096

分析

令S是某n-1位[0,k)组成的字符串。所有的S都是节点,则每个S都有k条出边,分别连向:S.Right(n-2)+0 S.Right(n-2)+1 ⋯ \cdots S.Right(n-2)+k-1;k条入边,分别连向0+S.Right(n-2) 1+S.Right(n-2) ⋯ \cdots k-1+S.Right(n-2)。
比如:n为3,k为3
12的出边:20 21 22
12的入边:01 11 21
n =3,k=2的所有边。
在这里插入图片描述
每条边都至少经过一次,由于是欧拉回路,所有可以所有边都只经过一次。
最后一条边是11$\rightarrow 10 则以 110 结尾。最后一条边是 11 10 则以110结尾。 最后一条边是 11 10则以110结尾。最后一条边是11\rightarrow$11 则以111结尾。
由于是欧拉回路,任意起点任意方向的边数一样。我们以字典顺序最小的为起点,访问字典顺序最小的边。箭头上面是最后n个字符。

代码

核心代码

class Solution {
public:string crackSafe(int n, int k) {if (1 == n){for (int i = 0; i < k; i++){m_strRet += '0' + i;}return m_strRet;}m_iK = k;int iMask = 1;for (int i = 1; i < n; i++){iMask *= k;}vector<std::queue<int>> vNeiBo(iMask);for (int i = 0; i < iMask; i++){int pre = i % (iMask / k);for (int j = 0; j < k; j++){vNeiBo[i].emplace(pre * k + j);}}DFS(vNeiBo, 0);m_strRet += string(n - 2, '0');	//DFS时,已经加了一个零	return string(m_strRet.rbegin(),m_strRet.rend());}void DFS(vector<std::queue<int>>& vNeiBo, int cur){while (vNeiBo[cur].size()){const auto next = vNeiBo[cur].front();vNeiBo[cur].pop();DFS(vNeiBo,next);}m_strRet += '0' + cur % m_iK;}int m_iK;string m_strRet;
};

测试用例

template<class T,class T2>
void Assert(const T& t1, const T2& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{int n ,k;{Solution sln;n = 2, k = 2;auto res = sln.crackSafe(n, k);Assert(strlen("01100"), res.length());}{Solution sln;n = 1, k = 2;auto res = sln.crackSafe(n, k);Assert(strlen("10"), res.length());}{Solution sln;n = 3, k = 2;auto res = sln.crackSafe(n, k);Assert(strlen("0011101000"), res.length());}{Solution sln;n = 2, k = 3;auto res = sln.crackSafe(n, k);Assert(strlen("0221120100"), res.length());}
}

2023年4月

class Solution {

public:
void dfs(int node) {
for (int i = 0; i < m_iK; i++)
{
const int iLine = node * 10 + i;
if (m_setHasDo.count(iLine))
{
continue;
}
m_setHasDo.emplace(iLine);
dfs(iLine% m_iRange);
m_strRet += i + ‘0’;
}
}

string crackSafe(int n, int k) {m_iRange = pow(10, n - 1);m_iK = k;dfs(0);m_strRet += string(n - 1, '0');return m_strRet;
}

private:
unordered_set m_setHasDo;
string m_strRet;
int m_iRange;
int m_iK;

};

2024年7月

class Solution {
public:
string crackSafe(int n, int k) {
string str;
if (1 == n)
{
for (int i = 0; i < k; i++)
{
str += (i + ‘0’);
}
return str;
}
m_iNodeNum = 1;
for (int i = 1; i < n; i++)
{
m_iNodeNum *= k;
}
m_vNeiB.resize(m_iNodeNum);
for (int i = 0; i < m_iNodeNum; i++)
{
for (int j = 0; j < k; j++)
{
m_vNeiB[i].emplace((i * k + j) % m_iNodeNum);
}
}
dfs(0);
string strRet(n - 1, ‘0’);
for (int i = m_vRevVisitNode.size() - 2; i >= 0; i–)
{
strRet += m_vRevVisitNode[i]%k + ‘0’;
}
return strRet;
}
void dfs(int cur)
{
while (m_vNeiB[cur].size())
{
int first = *m_vNeiB[cur].begin();
m_vNeiB[cur].erase(first);
dfs(first);
}
m_vRevVisitNode.emplace_back(cur);
}
int m_iNodeNum;
vector<std::unordered_set> m_vNeiB;
vector m_vRevVisitNode;
};

2024年8月

class Solution {
public:
string crackSafe(int n, int k) {
if (1 == n)
{
vector vRet;
for (int i = 0; i < k; i++)
{
vRet.emplace_back(i + ‘0’);
}
vRet.emplace_back(0);
return vRet.data();
}
m_iK = k;
const int iNodeNum = pow(k, n - 1);
m_vNeiBo.resize(iNodeNum);
for (int i = 0; i < iNodeNum; i++)
{
for (int j = 0; j < k; j++)
{
m_vNeiBo[i].emplace((i * k + j)%iNodeNum);
}
}
dfs(0);
m_vRet.pop_back();
for (int i = 0; i+1 < n; i++)
{
m_vRet.emplace_back(‘0’);
}
std::reverse(m_vRet.begin(), m_vRet.end());
m_vRet.emplace_back(0);
return m_vRet.data();
}
void dfs(int cur)
{
auto& curSet = m_vNeiBo[cur];
while (curSet.size())
{
const int next = *curSet.begin();
curSet.erase(next);
dfs(next);
}
m_vRet.emplace_back(cur%m_iK+‘0’);
}
int m_iK;
vector<set> m_vNeiBo;
vector m_vRet;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/498638.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第104讲:数据库分库分表的意义与实现策略(MyCat)

文章目录 1.分库分表的目的2.分库分表的拆分策略2.1.垂直拆分2.2.水平拆分 3.Mycat水平拆分的分片规则 1.分库分表的目的 互联网中的应用程序&#xff0c;随着公司的发展&#xff0c;应用系统的使用人数、数据量都再持续增长&#xff0c;数据库层面就会产生一定的瓶颈。 如果…

Qt篇——QTableWidget选中多行右键删除

效果如图&#xff1a; 代码如下&#xff1a; 头文件中&#xff1a; QTableWidgetItem *selectedItem; //表格被选中的一行 QMenu* originDataTableContextMenu; //表格右键菜单 QAction* originDataTableActionDel; //表格右键菜单…

vue中css语法错误

错误类型&#xff1a;&#xff08;有很多行出现这个错误&#xff09; 问了问GPT&#xff1a; 相关博客&#xff1a;SassError: expected selector报错 ::v-deep 替换 /deep/_expected selector root stylesheet-CSDN博客

C语言之操作符详解

文章目录 一、算术操作符二、移位操作符1、 原码、反码、补码2、左移操作符3、右移操作符 三、位操作符1、按位与【&】2、按位或【|】3、按位异或【^】4、按位取反【~】5、两道面试题6、进制定位将变量a的第n位置为1将变量a的第n位置为0 四、赋值操作符1、复合赋值符 五、单…

List集合的Stream流式操作实现数据类型转换

问题现象&#xff1a; 最近在项目中&#xff0c;有一些逻辑想用List集合的Stream流式操作来快速实现&#xff0c;但由于之前没做好学习笔记和总结&#xff0c;导致一时间想不起来&#xff0c;只能用本方法来解决&#xff0c;如下&#xff1a; 可以看出来代码量是比较冗长的&…

跟着野火学FreeRTOS:第二段(堆存储管理)

F r e e R T O S FreeRTOS FreeRTOS从版本 V 9.0.0 V9.0.0 V9.0.0开始&#xff0c;内核对象所用的存储空间可以在编译时静态分配或在运行时动态分配&#xff0c;早期的版本不同时支持静态分配和动态分配&#xff0c;这里讲到的堆存储管理是和动态分配相关的。从版本 V 9.0.0 V9…

详细讲解:文物预防性保护解决方案的目标

一、文物预防性保护方案的系统目标 可移动文物预防性保护监测与调控系统,是博物馆开展科学、有效的预防性保护策略的关键工程&#xff0c;整套系统包括监测系统平台建设、调控设备部署、数据传输设备部署和环境数据监测设备安装工程。项目完成时将达到以下目标: 1)构建覆盖全…

C语言数据结构基础-单链表

1.链表概念 在前面的学习中&#xff0c;我们知道了线性表&#xff0c;其中逻辑结构与物理结构都连续的叫顺序表&#xff0c;那么&#xff1a; 链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。 2.链表组…

单点登录的三种方式

前言 在B/S系统中&#xff0c;登录功能通常都是基于Cookie 来实现的。当用户登录成功后&#xff0c;一般会将登录状态记录到Session中&#xff0c;或者是给用户签发一个 Token&#xff0c;无论哪一种方式&#xff0c;都需要在客户端保存一些信息(Session ID或Token)&#xff0…

【Java程序设计】【C00317】基于Springboot的智慧社区居家养老健康管理系统(有论文)

基于Springboot的智慧社区居家养老健康管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的智慧社区居家养老健康管理系统设计与实现&#xff0c;本系统有管理员、社区工作人员、医生以及家属四种角色权限 管…

嵌入式学习第二十二天!(线程间通信)

线程间通信&#xff1a; 线程间通信&#xff0c;通过利用全局变量&#xff0c;来实现通信&#xff0c;但是在通信过程中使用这些变量可能会导致资源竞争&#xff0c;那么就需要使用到互斥锁和信息量&#xff0c;辅助我们实现线程的通信。 1. 线程分离属性&#xff1a; 线程结束…

简单版 git快速上手使用 clone项目 新建/切换分支 提交修改

Git是一个广泛使用的版本控制系统&#xff0c;允许多个用户跟踪文件的更改&#xff0c;并协作开发项目。 首先确定自己电脑已经安装了git&#xff0c;具体安装步骤请查找教程&#xff0c;应该不难。 以windows电脑为例&#xff0c;安装完后在搜索栏搜索git会出现 先解释一下这…