这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战
难度: 中等
题目描述
假设有从 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 的自然序列,满足整除关系: i % nums[i] = 0 或 nums[i] % i = 0(i 为第 i 个位置)。 由于看到数据范围 N <= 15,因此很容易想到这道题用深搜(DFS)去做。
class Solution:
def countArrangement(self, n: int) -> int:
ret = 0
matchs = collections.defaultdict(list)
for i in range(1, n + 1):
for j in range(1, n + 1):
if i % j == 0 or j % i == 0:
matchs[i].append(j)
# print(matchs)
seen = set()
def dfs(index):
nonlocal ret
if index > n:
ret += 1
return
for v in matchs[index]:
if v not in seend:
seend.add(v)
dfs(index + 1)
seen.discard(v)
dfs(1)
return ret
思路二(带记忆DFS)
class Solution:
def countArrangement(self, N: int) -> int:
import functools
@functools.lru_cache(None)
def dfs(i, cur):
if i == N + 1:
return 1
res = 0
for k in range(N):
num = 1 << k
if not cur & num and ((k + 1) % i == 0 or i % (k + 1) == 0):
res += dfs(i + 1, cur | num)
return res
return dfs(1, 0)