具体思想:
DP记忆化搜索;
难点在于两地方:
- DP思路;
- 如何利用位计算;
利用位bit来表达那些位置用了,不用关心具体用的是谁;
枚举1~n位,此时表示考虑第i位能有多少收益,dp[i][state];
其中state代表状态值;
枚举所有状态,但是注意,由于现在枚举到i位,总共填进去i个数,因此state是否判断需要统计已用多少个,是否是i个,也就是统计每个状态的bit 1的个数;
对合法状态进行1~n的枚举,首先选择填进去的数字要符合题目条件,其次对于该数,也必须在state表示已用过;
加起来就行;
具体代码:
1.回溯:
class Solution {
public:
int countArrangement(int n) {
vector<bool>vis(n,false);
int ret=0;
dfs(vis,n,1,ret);
return ret;
}
void dfs(vector<bool>& vis,int& n,int cnt,int& ret){
if(cnt==n+1){
ret++;
return;
}
for(int i=1;i<=n;i++){
if(vis[i])
continue;
if(i%cnt!=0&&cnt%i!=0)
continue;
vis[i]=true;
dfs(vis,n,cnt+1,ret);
vis[i]=false;
}
}
};
2.状态DP:
class Solution {
public:
int countArrangement(int n) {
int mask=1<<n;
vector<vector<int>>dp(n+1,vector<int>(mask,0));
dp[0][0]=1;
for(int i=1;i<=n;i++){
//枚举前i个位置;
for(int j=0;j<mask;j++){
int num = __builtin_popcount(j);
if(num!=i)
continue;
for(int k=1;k<=n;k++){
if((1<<(k-1)&j)!=0&&(k%i==0||i%k==0)){
dp[i][j]+=dp[i-1][(~(1<<(k-1)))&j];
}
}
}
}
return dp[n][mask-1];
}
};