【LeetCode 每日一题】2741. 特别的排列

250 阅读1分钟

2741. 特别的排列

难度:中等

时间:2023/06/30


给你一个下标从 0  开始的整数数组 nums ,它包含 n 个 互不相同  的正整数。如果 nums 的一个排列满足以下条件,我们称它是一个特别的排列:

  • 对于 0 <= i < n - 1 的下标 i ,要么 nums[i] % nums[i+1] == 0 ,要么 nums[i+1] % nums[i] == 0 。

请你返回特别排列的总数目,由于答案可能很大,请将它对 10^9 + 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] <= 10^9

class Solution {
public:
    int specialPerm(vector<int>& nums) {
        int n = nums.size();
        int dp[1 << n][n];
        int MOD = 1e9 + 7;
        memset(dp, 0, sizeof dp);
        for (int i = 0; i < (1 << n); ++i) {
            for (int j = 0; j < n; ++j) {
                if ((i >> j) & 1) {
                    int ni = i ^ (1 << j);
                    if (ni == 0) {
                        dp[i][j] = 1;
                        continue;
                    }
                    for (int k = 0; k < n; ++k) {
                        if (nums[k] % nums[j] == 0 || nums[j] % nums[k] == 0) 
                            dp[i][j] = (dp[i][j] + dp[ni][k]) % MOD;
                    }
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < n; ++i) 
            ans = (ans + dp[(1 << n) - 1][i]) % MOD;
        return ans;
    }
};

总结: 对于 nums 数组中任意一个元素都有选与不选两种状态,因此考虑使用状态压缩dp进行求解。

设置二维数组 dp[1 << n][n] ,第一维表示 nums 数组中所有元素是否被选择的状态,对应的位数上为 1 则表示被选;第二维 j 表示所有被选的元素组成的序列中,以 nums[j] 为最后一个的序列。

则状态转移方程为: dp[i][j] = sum(dp[i ^ (1 << j)][k],其中 i ^ (1 << j) 表示不选取第 j 位元素的状态,将所有该状态下以任意元素 nums[k] 结尾的序列个数累加起来。

class Solution:
    def specialPerm(self, nums: List[int]) -> int:
        n = len(nums)
        mod = 10 ** 9 + 7
        @lru_cache(None)
        def dfs(state, x):
            if not state:
                return 1
            ret = 0 
            for j in range(n):
                if (1 << j) & state and (x % nums[j] == 0 or nums[j] % x == 0):
                    ret = (ret + dfs(state ^ (1 << j), nums[j])) % mod
            return ret % mod
        return dfs((1 << n) - 1, 0)

1.不用考虑组合顺序,只用在乎最后一个元素就可以,只用判断当前要添加的数是否可可以接入之前的状态(题目要求的规则) 2 由于数据范围很小 可以使用状态压缩就可以 2.1 每个数据都给一个问题的二进制编码 例如 011101 表示状态 从右向左开始遍历 最右侧是 0位 以此类推 dp[011101][0] 可以从 dp[011100][x]推断来下 x表示 011100对应位的编码 且满足题目要求 3 最后累加 dp[1<nums.length-1][0~nums.length-1]