526. 优美的排列(题目地址)
假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:
1. 第 i 位的数字能被 i 整除
2. i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?
输入: 2
输出: 2
解释:
第 1 个优美的排列是 [1, 2]:
第 1 个位置(i=1)上的数字是1,1能被 i(i=1)整除
第 2 个位置(i=2)上的数字是2,2能被 i(i=2)整除
第 2 个优美的排列是 [2, 1]:
第 1 个位置(i=1)上的数字是2,2能被 i(i=1)整除
第 2 个位置(i=2)上的数字是1,i(i=2)能被 1 整除
数据说明:N是一个正整数,并且不会超过15。
这道题由于它的N比较小,一开始我想着去找规律,一个个将他全部计算出来,而实际上这确实也是个方法,并且也有许多人将它实现了。但我估计他们也是先通过回溯算法或者是状态dp先把答案算出来,然后再直接提交答案。
本题符合经典dfs回溯的特点,我们可以从左到右依次确定每个位置上的数,到最后一个数时在往回重新更改。具体来说就是dfs(index,n)确定index位置上的数,然后继续搜索dfs(index+1,n)。为了提高dfs的效率,我们设置一个vis数组将搜索过的数标记起来,这样可以减少dfs的次数,然后在回溯到这个标记的位置时,将它取消标记。为了得到每一个index上可放置的数有哪些,我们在dfs之前先设置一个math二维数组,将index位置对应的数放于math中的相应行中。
具体表达可看代码:
class Solution {
public:
//经典dfs回溯算法
int ans=0;
vector<vector<int>> mask;
vector<int> vis;
void dfs(int index,int n){
if(index==n+1){
ans++;
return;
}
for(auto ch:mask[index]){
if(!vis[ch]){
vis[ch]=1;
dfs(index+1,n);
vis[ch]=0;
}
}
}
int countArrangement(int n) {
mask.resize(n+1);
vis.resize(n+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i%j==0||j%i==0)
mask[i].emplace_back(j);
}
}
dfs(1,n);
return ans;
}
};