"""
假設有從 1 到 N 的 N 個整數,如果從這 N 個數字中成功構造出一個數組,使得數組的第 i 位 (1 <= i <= N) 滿足如下兩個條件中的一個,我們就稱這個數組爲一個優美的排列。條件:
第 i 位的數字能被 i 整除
i 能被第 i 位上的數字整除
現在給定一個整數 N,請問可以構造多少個優美的排列?
來源:力扣(LeetCode)
"""
#直接暴力枚舉所有排列判斷會超時
class Solution(object):
def solve(self, start, end, strlist, reslist):
if(start == end):
flag = 0
for j in range(end+1):
if(strlist[j]%(j+1)!=0 and (j+1)%strlist[j]!=0):
flag = 1
break;
if(flag == 0):
reslist.append(strlist)
for i in range(start,end+1):
strlist[i], strlist[start] = strlist[start], strlist[i]
self.solve(start+1, end, strlist, reslist)
strlist[i], strlist[start] = strlist[start], strlist[i]
def countArrangement(self, N):
"""
:type N: int
:rtype: int
"""
strlist = list(range(1,N+1))
#print(strlist)
reslist = list()
self.solve(0,N-1,strlist, reslist)
#print(reslist)
return len(reslist)
#優化後的暴力搜索
#在每次填數的時候就判斷
class Solution(object):
c=0
def solve(self, start, strlist):
#print(start)
if(start == len(strlist)):
self.c = self.c + 1
for i in range(start,len(strlist)):
#if(strlist[i]%(start+1)==0 or (start+1)%strlist[i]==0) and (strlist[start]%(i+1)==0 or (i+1)%strlist[start]==0):
strlist[i], strlist[start] = strlist[start], strlist[i]
if(strlist[start]%(start+1)==0 or (start+1)%strlist[start]==0):
self.solve(start+1, strlist)
strlist[i], strlist[start] = strlist[start], strlist[i]
def countArrangement(self, N):
"""
:type N: int
:rtype: int
"""
strlist = list(range(1,N+1))
print(strlist)
self.solve(0,strlist)
#print(reslist)
return self.c
#回溯
#用bool數組表示是否已經使用過當前數,每次從沒有使用過的數拿一個放到pos,如果滿足條件,繼續下一個,否則換數。
public class Solution {
int count = 0;
public int countArrangement(int N) {
boolean[] visited = new boolean[N + 1];
calculate(N, 1, visited);
return count;
}
public void calculate(int N, int pos, boolean[] visited) {
if (pos > N)
count++;
for (int i = 1; i <= N; i++) {
if (!visited[i] && (pos % i == 0 || i % pos == 0)) {
visited[i] = true;
calculate(N, pos + 1, visited);
visited[i] = false;
}
}
}
}
# dp,狀態壓縮
"""
思路:用一個n位的二進制數表示二進制中爲1的數字已任意順序放在數組的前m位(m爲該二進制數中1的個數)。
例子:二進制數010101,第1,3,5位是1,一共3個1,所以表示1,3,5以任意順序放在數組前3位。
狀態:dp[n]表示二進制數n代表的所有排列中有效情況的數量。
更新狀態:對於二進制中爲0的位,判斷是否可以作爲下一個放入數組中的數,若是則更新dp[n | (1 << j)] += dp[n]
例子:010101中,第2位爲0,判斷其是否可放在數組第4位(因爲數組已有3個數),顯然可以(4%2==0),更新dp[010101 | (1 << (2-1))] += dp[010101];
即dp[010111] += dp[010101];
"""
int countArrangement(int N) {
vector<int> dp(1 << N,0);
//每個數都能放在第1位
for(int i = 0;i < N;++i)
{
dp[1 << i] = 1;
}
for(int i = 0;i < (1 << N);++i)
{
int index = 1;
int temp = i;
//計算1的個數
while(temp)
{
temp &= temp-1;
++index;
}
for(int j = 0;j < N;++j)
{
//二進制位爲0(未被選取),且能放在第index位
if(!(i & (1 << j)) && ((j+1) % index == 0 || index % (j+1) == 0))
{
dp[1 << j | i] += dp[i];
}
}
}
return dp[(1 << N) -1];
}