【LetMeFly】2741.特别的排列:状压DP 力扣题目链接:https://leetcode.cn/problems/special-permutations/
给你一个下标从 0 开始的整数数组 nums
,它包含 n
个 互不相同 的正整数。如果 nums
的一个排列满足以下条件,我们称它是一个特别的排列:
对于 0 <= i < n - 1
的下标 i
,要么 nums[i] % nums[i+1] == 0
,要么 nums[i+1] % nums[i] == 0
。
请你返回特别排列的总数目,由于答案可能很大,请将它对 109 + 7
取余 后返回。
示例 1:
输入: nums = [2,3,6]
输出: 2
解释: [3,6,2] 和 [2,6,3] 是 nums 两个特别的排列。
示例 2:
输入: nums = [1,4,3]
输出: 2
解释: [3,1,4] 和 [4,1,3] 是 nums 两个特别的排列。
提示:
2 <= nums.length <= 14
1 <= nums[i] <= 109
解题方法:状态压缩的动态规划 需要明白的是,若要看 “在特别排列[a, b, c]
的基础上添加元素d
生成的[a, b, c, d]
”是否为特别排列,只需要判断c
和d
是否能整除或被整除即可。
因此,对于一个特别排列,我们只关心这个排列的最后一个数字 以及这个排列中已经有了哪些数字 。
对于“这个排列中已经有了哪些数字”,我们可以使用“一个整数二进制下的低$n$位”来表示。
因此,我们可以定义一个DP
数组,dp[state][last]
表示排列中出现的数字们为state
,排列最后一个数字为last
时的“特别排列”数。
这个数是怎么得到的呢?假设prev
在当前排列中(state & (1 << prev) ≠ 0
)且prev
和last
是倍数关系,那么这个排列可以由“这个排列移除last
的最后一个数为prev
的排列”拼接上last
得到。
因此有状态转移方程:$dp[state][last] = \sum_{prev\in state} dp[state - (1 << last)][prev]$。
时间复杂度$O(2^nn^2)$
空间复杂度$O(2^nn)$
AC代码 C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const static long long MOD = 1e9 + 7 ;class Solution {public : int specialPerm (vector<int >& nums) { int n = nums.size (); vector<vector<long long >> dp (1 << n, vector <long long >(n, 0 )); for (int i = 0 ; i < n; i++) { dp[1 << i][i] = 1 ; } for (int state = 0 ; state < (1 << n); state++) { for (int prev = 0 ; prev < n; prev++) { for (int last = 0 ; last < n; last++) { if ((state & (1 << last)) && (state & (1 << prev)) && last != prev && (nums[last] % nums[prev] == 0 || nums[prev] % nums[last] == 0 )) { dp[state][last] = (dp[state][last] + dp[state ^ (1 << last)][prev]) % MOD; } } } } long long ans = 0 ; for (int last = 0 ; last < n; last++) { ans = (ans + dp[(1 << n) - 1 ][last]) % MOD; } return ans; } };
Python 附上一个Python超时版本。不想提前判断剪枝优化了。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from typing import List MOD = 1_000_000_007 class Solution : def specialPerm (self, nums: List [int ] ) -> int : n = len (nums) dp = [[0 for _ in range (n)] for __ in range (1 << n)] for i in range (n): dp[1 << i][i] = 1 for state in range (1 << n): for last in range (n): for prev in range (n): if (state & (1 << last)) and (state & (1 << prev)) and (nums[prev] % nums[last] == 0 or nums[last] % nums[prev] == 0 ): dp[state][last] = (dp[state][last] + dp[state ^ (1 << last)][prev]) % MOD ans = 0 for i in range (n): ans = (ans + dp[(1 << n) - 1 ][i]) % MOD return ansif __name__ == '__main__' : print (Solution.specialPerm('' , [838335396 , 241654240 , 937115884 , 795934157 , 907282921 , 71642053 , 242720010 , 16417709 , 706807579 , 752842522 , 162230770 , 425078819 , 793563691 , 522087056 ]))
反正也每人看我题解(bushi)。
同步发文于CSDN和我的个人博客 ,原创不易,转载经作者同意后请附上原文链接 哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/140000372