leetcode526. 优美的排列
题目链接:https://leetcode-cn.com/problems/beautiful-arrangement/
说实话一开始没懂这个题目的意思,是必须用到N个数字,还是从N个数字中选取任意个数字组成数组,看了题解,指的是前者。
我的思路是:1.枚举所有数字的排列组合2.找出符合条件的数组
回溯
回溯法可以被认为是一个有过剪枝的DFS过程。
这里科普一下递归、回溯的dfs
原文链接:https://blog.csdn.net/fengchi863/article/details/80086915
- 递归是一种算法结构,回溯是一种算法思想。
- 一个递归就是在函数中调用函数本身来解决问题。
- 回溯就是通过不同的尝试来生成问题的解,有点类似于穷举,但是和穷举不同的是回溯会“剪枝”。
剪枝的意思也就是说对已经知道错误的结果没必要再枚举接下来的答案了,比如一个有序数列1,2,3,4,5,我要找和为5的所有集合,从前往后搜索我选了1,然后2,然后选3的时候发现和已经大于预期,那么4,5肯定也不行,这就是一种对搜索过程的优化。
回溯搜索是深度优先搜索(DFS)的一种。对于某一个搜索树来说(搜索树是起记录路径和状态判断的作用),回溯和DFS,其主要的区别是,回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树。
为了减少存储空间,在深度优先搜索中,用标志的方法记录访问过的状态,这种处理方法使得深度优先搜索法与回溯法没什么区别了。
递归的一般结构:
void f()
{
if(符合边界条件)
{
///////
return;
}
//某种形式的调用
f();
}
回溯的一般结构:
void DFS(int 当前状态)
{
if(当前状态为边界状态)
{
记录或输出
return;
}
for(i=0;i<n;i++) //横向遍历解答树所有子节点
{
//扩展出一个子状态。
修改了全局变量
if(子状态满足约束条件)
{
dfs(子状态)
}
恢复全局变量//回溯部分
}
}
对于本题,可以用回溯法来解决,从左到右依次向目标排列中放入数即可。
定义函数backtrack(index,n),表示尝试向位置index放入数,其中n表示排列的长度。首先需要找到一个符合条件的未被使用过的数,然后递归地执行backtrack(index+1,n),当该函数执行完毕,回溯到当前层,我们再尝试下一个符合条件的未被使用过的数即可。
回溯过程中,我们可以用vis数组标记哪些数被使用过,每当我们选中一个数x,就将vis[x]标记为true,回溯完成后,我们再将其置为false。
class Solution {
public:
vector<bool>visited;
int ans=0;
void backtrack(int n,int index){
if(index == n+1){//当找到n个符合要求的序列时,满足要求,ans++
ans++;
}
for(int i = 1;i <= n; i++){//枚举寻找第index个满足要求且没有遍历过的元素
if( !visited[i] && (i%index==0||index%i==0) ){
visited[i]=true;//标记为遍历过
backtrack(n, index+1);//寻找第index+1个满足要求的元素
visited[i]=false;//回溯
}
}
}
int countArrangement(int n) {
visited.resize(n+1);
backtrack(n, 1);
return ans;
}
};
为了优化回溯效率,我们可以预处理每个位置符合条件的数有哪些,用二维数组match保存。当我们尝试向位置index放入数时,只需要遍历match[index]即可。
class Solution {
public:
vector<vector<int>> match;
vector<int> vis;
int num;
void backtrack(int index, int n) {
if (index == n + 1) {
num++;
return;
}
for (auto &x : match[index]) {
if (!vis[x]) {
vis[x] = true;
backtrack(index + 1, n);
vis[x] = false;
}
}
}
int countArrangement(int n) {
vis.resize(n + 1);
match.resize(n + 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i % j == 0 || j % i == 0) {
match[i].push_back(j);
}
}
}
backtrack(1, n);
return num;
}
};
原文地址:https://www.cnblogs.com/LingFengJ/p/15149855.html
- Spring Cloud Edgware新特性之七:可选的EnableDiscoveryClient注解
- 【LEETCODE】模拟面试-46. Permutations
- CentOS6 Upgrade Python
- Emacs setup for Go Development
- 【LEETCODE】模拟面试-39. Combination Sum
- Docker系列教程12-使用Maven插件构建Docker镜像
- Linux nohup 用法
- 【LEETCODE】模拟面试-84-Largest Rectangle in Histogram
- Docker系列教程11-使用Nexus管理Docker镜像
- Mac 配置终端环境
- 【LEETCODE】模拟面试-101-Symmetric Tree
- Docker系列教程10-使用Docker Registry管理镜像
- webapp开发调试环境--weinre配置
- AWK 深入浅出教程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 什么是运维眼中可部署的软件架构
- 2020-09-03:裸写算法:回形矩阵遍历。
- Java并发编程系列34 | 深入理解线程池(下)
- MySQL 8.0新特性 — 密码管理
- 聊聊claudb的NotificationManager
- windows下安装Postman
- 【Pytorch 】笔记七:优化器源码解析和学习率调整策略
- 【Pytorch 】笔记六:初始化与 18 种损失函数的源码解析
- logstash index 生成时间晚 8 小时
- top 命令查看cpu利用率超过500%
- 【Pytorch 】笔记四:Module 与 Containers 的源码解析
- 控制pod内container执行顺序的几种姿势
- 本地部署istio多集群(共享控制面)
- elasticsearch unassigned shard
- 【Pytorch 】笔记五:nn 模块中的网络层介绍