leetcode 996 正方形数组的数目 简明解法
.996. 正方形数组的数目
给定一个非负整数数组 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
题目给的例子太简单,我们先构建一个稍复杂点的例子:
A=[17,8,1,3,1,15]
我们看怎样求解。
1.定义全局变量:
unordered_map<int, int> m;
unordered_map<int, unordered_set<int>> data;
int sz;
int ret;
其中,sz和ret赋值:
int numSquarefulPerms(vector<int>& A) {
sz = A.size();
ret = 0;
2.构造2个map
找出相加能得到完全平方数的配对:1+3=4, 1+8=9, 1+15=16, 8+17=25
之后,我们构造2个map:
第一个map用来存A中各个数出现的次数。
定义前面已经给出:unordered_map<int,int> m;
for (auto i : A)
m[i]++;
结果如下:
key | value |
---|---|
1 | 2 |
3 | 1 |
8 | 1 |
15 | 1 |
17 | 1 |
第二个map存各个数可以组成配对的数:
定义:unordered_map<int,unordered_set<int>> data;
for (int i = 0; i < sz - 1; ++i)
for (int j = i + 1; j < sz; ++j)
{
double v = A[i] + A[j];
v = sqrt(v);
if (v == floor(v))
{
data[A[i]].insert(A[j]);
data[A[j]].insert(A[i]);
}
}
两层for循环对数组A得到了2个元素的组合,v为选出的2个数的和,若sqrt(v)取整等于自身,即小数部分为0,代表这2个数的和为完全平方数。
map的key为A的每个元素,value为可以和key配对的数。
结果如下:
key | value |
---|---|
1 | 3,8,15 |
3 | 1 |
8 | 1,7 |
15 | 1 |
17 | 8 |
这样,我们从data的1出发,1可以和3,8,5配对,先选3,得到:
1→3
同时,m[1]- -,m[3]- -,代表1和3已经各用掉一个。
我们再看data[3],3可以和1配对,此时m[1]==1,还可以用1个1,得到:
1→3→1
现在看data[1]={3,8,5},m[3]==0代表3没有了,m[8]==1,可以用8,得到:
1→3→1→8
现在data[8]={1},但是现在m[1]==0,没有1可用了,说明这个序列不正确。
按照上面的思路,我们依次从data的每一个key出发,构建递归树,在此过程中加和有效序列的数量,得到最终结果。
这样,我们可以构造函数:
void fun(int v,int count)
{
if (count == sz)
{
ret++;
return;
}
unordered_set<int> s = data[v];
for (auto i : s)
{
if (m[i] > 0)
{
m[i]--;
fun(i, count+1);
m[i]++;
}
}
}
输入参数v为A中元素的值,count为当前已经选取的元素数量,用dfs的方式构建递归树。
最后,因为fun的输入参数为v,所以在主函数中遍历data的各个key,依次调用fun:
for (auto &p : data)
{
int v = p.first;
m[v]--;
fun(v, 1);
m[v]++;
}
return ret;
通过用时8ms。
完整代码:
class Solution {
public:
unordered_map<int, int> m;
unordered_map<int, unordered_set<int>> data;
int sz;
int ret;
void fun(int v,int count)
{
if (count == sz)
{
ret++;
return;
}
unordered_set<int> s = data[v];
for (auto i : s)
{
if (m[i] > 0)
{
m[i]--;
fun(i, count+1);
m[i]++;
}
}
}
int numSquarefulPerms(vector<int>& A) {
sz = A.size();
ret = 0;
for (auto i : A)
m[i]++;
for (int i = 0; i < sz - 1; ++i)
for (int j = i + 1; j < sz; ++j)
{
double v = A[i] + A[j];
v = sqrt(v);
if (v == floor(v))
{
data[A[i]].insert(A[j]);
data[A[j]].insert(A[i]);
}
}
for (auto &p : data)
{
int v = p.first;
m[v]--;
fun(v, 1);
m[v]++;
}
return ret;
}
};