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]++;

结果如下:

keyvalue
12
31
81
151
171

第二个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配对的数。
结果如下:

keyvalue
13,8,15
31
81,7
151
178

这样,我们从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;
}
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值