LeetCode 2741.特别的排列:状压DP

【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]"是否为特别排列,只需要判断cd是否能整除或被整除即可。

因此,对于一个特别排列,我们只关心这个排列的最后一个数字 以及这个排列中已经有了哪些数字

对于"这个排列中已经有了哪些数字",我们可以使用"一个整数二进制下的低 n n n位"来表示。

因此,我们可以定义一个DP数组,dp[state][last]表示排列中出现的数字们为state,排列最后一个数字为last时的"特别排列"数。

这个数是怎么得到的呢?假设prev在当前排列中(state & (1 << prev) ≠ 0)且prevlast是倍数关系,那么这个排列可以由"这个排列移除last的最后一个数为prev的排列"拼接上last得到。

因此有状态转移方程: d p [ s t a t e ] [ l a s t ] = ∑ p r e v ∈ s t a t e d p [ s t a t e − ( 1 < < l a s t ) ] [ p r e v ] dp[state][last] = \sum_{prev\in state} dp[state - (1 << last)][prev] dp[state][last]=∑prev∈statedp[state−(1<<last)][prev]。

  • 时间复杂度 O ( 2 n n 2 ) O(2^nn^2) O(2nn2)
  • 空间复杂度 O ( 2 n n ) O(2^nn) O(2nn)

AC代码

C++
cpp 复制代码
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超时版本。不想提前判断剪枝优化了。。。

python 复制代码
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 ans


if __name__ == '__main__':
    print(Solution.specialPerm('', [838335396, 241654240, 937115884, 795934157, 907282921, 71642053, 242720010, 16417709, 706807579, 752842522, 162230770, 425078819, 793563691, 522087056]))

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

Tisfy:https://letmefly.blog.csdn.net/article/details/140000372

相关推荐
weixin_3077791311 分钟前
软件演示环境动态扩展与成本优化:基于目标跟踪与计划扩展的AWS Auto Scaling策略
算法·云原生·云计算·aws
Carl_奕然11 分钟前
【机器视觉】一文掌握常见图像增强算法。
人工智能·opencv·算法·计算机视觉
放羊郎12 分钟前
人工智能算法优化YOLO的目标检测能力
人工智能·算法·yolo·视觉slam·建图
无敌最俊朗@1 小时前
友元的作用与边界
算法
Miraitowa_cheems1 小时前
LeetCode算法日记 - Day 104: 通配符匹配
linux·数据结构·算法·leetcode·深度优先·动态规划
程序员东岸1 小时前
从零开始学二叉树(上):树的初识 —— 从文件系统到树的基本概念
数据结构·经验分享·笔记·学习·算法
甄心爱学习2 小时前
数据挖掘11-分类的高级方法
人工智能·算法·分类·数据挖掘
爪哇部落算法小助手3 小时前
每日两题day44
算法
不穿格子的程序员4 小时前
从零开始写算法——二分-搜索二维矩阵
线性代数·算法·leetcode·矩阵·二分查找
Kuo-Teng4 小时前
LeetCode 19: Remove Nth Node From End of List
java·数据结构·算法·leetcode·链表·职场和发展·list