假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:
- 第 i 位的数字能被 i 整除
- i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?
示例1:
输入: 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个数1~N,那么对于第一个数有N种情况,第二个数有N-1种情况。。。以此类推,用一个记忆数组memo来判断是否访问过当前数组的下标,如果没有访问过并且当前下标和下标对应的值可以整除或被整除,那么继续选择下一个数字,否则当前数字重选,这样做可以大大减少搜索的次数。
核心代码如下:
for (int i = 1; i <= N; i++) {
if(!memo[i] && (pos%i==0 || i%pos==0)){
memo[i] = 1;
countArrangementCore(N, pos + 1, memo,res);
memo[i] = 0;
}
}
对于每位(当前为第i位)数字都从1遍历到N,只是下一位(i+1位)数字是否选择由memo控制,那么我们的截止条件便是如果当前位置pos访问到了word的最后一位的下一位,那么把结果res++。
参考代码:
class Solution {
public:
void countArrangementCore(int N,int pos, vector<bool> &memo,int &res) {
if (pos>N) {
res++;
return;
}
for (int i = 1; i <= N; i++) {
if(!memo[i] && (pos%i==0 || i%pos==0)){
memo[i] = 1;
countArrangementCore(N, pos + 1, memo,res);
memo[i] = 0;
}
}
}
int countArrangement(int N) {
if (N <= 1) return 1;
vector<bool> memo(N+1,0);
int res = 0;
countArrangementCore(N, 1,memo,res);
return res;
}
};