特别的排列,状压DP

特别的排列
记忆化搜索,dfs(s,i)代表当前可选的集合为s,上一个选择的是nums[i]

typedef long long LL;
class Solution {
public:

  
    int specialPerm(vector<int>& nums) {
  
        int n = nums.size();
        int u = (1 << n) - 1;

        vector<vector<LL>> f(u, vector<LL>(n,-1));
        function<LL(int,int) > dfs = [&](int s, int i)
        {
            if(s == 0)return 1LL;
            LL& res = f[s][i];
            if(res != -1)return res;
            res = 0;
            for(int j = 0; j < n; j ++)
            {
                if( ((s >> j) & 1) && (nums[i] % nums[j] == 0 || nums[j] % nums[i] == 0)    )
                {
                    res += dfs(s ^ (1 << j), j);
                } 
            }
            return res;
        };
        LL res = 0;
        for(int i = 0; i < n; i ++)
        {
            
            res += dfs(u ^ (1 << i),i);
        }
        return res % 1'000'000'007;
    }
};

自底向上,状压DP

f[s][i]表示当前当前可选的集合为s,上一个选择的是nums[i]

typedef long long LL;
class Solution {
public:

  
    int specialPerm(vector<int>& nums) {
  
        int n = nums.size();
        int u = (1 << n) - 1;

        vector<vector<LL>> f(u, vector<LL>(n,0));
        ranges::fill(f[0], 1LL);

        for(int s = 1; s < u; s ++)
        {
            for(int i = 0; i < n; i ++)
            {
                if(s >> i & 1)continue;  //f[s][i],其中s表示已经选的集合,这里表示是即将选nums[i],但是当前集合没有nums[i]
            
                for(int j = 0; j < n; j ++)
                {
                    if((s >> j & 1) && (nums[j] % nums[i] == 0 || nums[i] % nums[j] == 0 ))
                    {
                        f[s][i] += f[s ^ (1 << j)][j];//这次选择nums[i],上一个选的是nums[j]
                    }
                }
            }
            
        }

       
        LL res = 0;
        for(int i = 0; i < n; i ++)
        {
            
            res += f[u ^ (1 << i)][i];
        }
        return res % 1'000'000'007;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值