特别的排列
记忆化搜索,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;
}
};