给定一个非负整数数组 A
,如果该数组每对相邻元素之和是一个完全平方数,则称这一数组为正方形数组。
返回 A 的正方形排列的数目。两个排列 A1
和 A2
不同的充要条件是存在某个索引 i
,使得 A1[i] != A2[i]。
示例 1:
输入:[1,17,8] 输出:2 解释: [1,8,17] 和 [17,8,1] 都是有效的排列。
示例 2:
输入:[2,2,2] 输出:1
提示:
1 <= A.length <= 12
0 <= A[i] <= 1e9
方法一:二进制状态压缩 + 动态规划
注意到,数组
我们定义
接下来,我们考虑如何进行状态转移。
假设当前所选的数字的状态为
最后,我们还需要除以每个数字出现的次数的阶乘,因为我们在枚举
时间复杂度
class Solution:
def numSquarefulPerms(self, nums: List[int]) -> int:
n = len(nums)
f = [[0] * n for _ in range(1 << n)]
for j in range(n):
f[1 << j][j] = 1
for i in range(1 << n):
for j in range(n):
if i >> j & 1:
for k in range(n):
if (i >> k & 1) and k != j:
s = nums[j] + nums[k]
t = int(sqrt(s))
if t * t == s:
f[i][j] += f[i ^ (1 << j)][k]
ans = sum(f[(1 << n) - 1][j] for j in range(n))
for v in Counter(nums).values():
ans //= factorial(v)
return ans
class Solution {
public int numSquarefulPerms(int[] nums) {
int n = nums.length;
int[][] f = new int[1 << n][n];
for (int j = 0; j < n; ++j) {
f[1 << j][j] = 1;
}
for (int i = 0; i < 1 << n; ++i) {
for (int j = 0; j < n; ++j) {
if ((i >> j & 1) == 1) {
for (int k = 0; k < n; ++k) {
if ((i >> k & 1) == 1 && k != j) {
int s = nums[j] + nums[k];
int t = (int) Math.sqrt(s);
if (t * t == s) {
f[i][j] += f[i ^ (1 << j)][k];
}
}
}
}
}
}
long ans = 0;
for (int j = 0; j < n; ++j) {
ans += f[(1 << n) - 1][j];
}
Map<Integer, Integer> cnt = new HashMap<>();
for (int x : nums) {
cnt.merge(x, 1, Integer::sum);
}
int[] g = new int[13];
g[0] = 1;
for (int i = 1; i < 13; ++i) {
g[i] = g[i - 1] * i;
}
for (int v : cnt.values()) {
ans /= g[v];
}
return (int) ans;
}
}
class Solution {
public:
int numSquarefulPerms(vector<int>& nums) {
int n = nums.size();
int f[1 << n][n];
memset(f, 0, sizeof(f));
for (int j = 0; j < n; ++j) {
f[1 << j][j] = 1;
}
for (int i = 0; i < 1 << n; ++i) {
for (int j = 0; j < n; ++j) {
if ((i >> j & 1) == 1) {
for (int k = 0; k < n; ++k) {
if ((i >> k & 1) == 1 && k != j) {
int s = nums[j] + nums[k];
int t = sqrt(s);
if (t * t == s) {
f[i][j] += f[i ^ (1 << j)][k];
}
}
}
}
}
}
long long ans = 0;
for (int j = 0; j < n; ++j) {
ans += f[(1 << n) - 1][j];
}
unordered_map<int, int> cnt;
for (int x : nums) {
++cnt[x];
}
int g[13] = {1};
for (int i = 1; i < 13; ++i) {
g[i] = g[i - 1] * i;
}
for (auto& [_, v] : cnt) {
ans /= g[v];
}
return ans;
}
};
func numSquarefulPerms(nums []int) (ans int) {
n := len(nums)
f := make([][]int, 1<<n)
for i := range f {
f[i] = make([]int, n)
}
for j := range nums {
f[1<<j][j] = 1
}
for i := 0; i < 1<<n; i++ {
for j := 0; j < n; j++ {
if i>>j&1 == 1 {
for k := 0; k < n; k++ {
if i>>k&1 == 1 && k != j {
s := nums[j] + nums[k]
t := int(math.Sqrt(float64(s)))
if t*t == s {
f[i][j] += f[i^(1<<j)][k]
}
}
}
}
}
}
for j := 0; j < n; j++ {
ans += f[(1<<n)-1][j]
}
g := [13]int{1}
for i := 1; i < 13; i++ {
g[i] = g[i-1] * i
}
cnt := map[int]int{}
for _, x := range nums {
cnt[x]++
}
for _, v := range cnt {
ans /= g[v]
}
return
}