作者推荐
本文涉及知识点
LeetCoce996正方形数组的数目
给定一个非负整数数组 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
动态规划
动态规划的状态表示
mask表示那些数字已经选取,end表示结束数字下标。 空间复杂度: O( (1 << 12) *12)。
dp[mask][end]记录符合条件的数量。
动态规划的转移方程
vNext记录所有能和end组成完全平方数的数字下标。
f o r n e x t : v N e x t { n e x t 已经处理忽略 ( 1 < < n e x t ) m a s k d p [ m a s k ∣ ( 1 < < n e x t ) ] [ n e x t ] + = d p [ m a s k ] [ e n d ] e l s e for \quad next:vNext \begin{cases} next已经处理忽略 & (1 << next) & mask \\ dp[mask|(1<<next)][next] += dp[mask][end] & else \\ \end{cases}fornext:vNext{next已经处理忽略dp[mask∣(1<<next)][next]+=dp[mask][end](1<<next)elsemask
动态规矩的初始状态
for i = 0 : n-1 dp[1 << 1][i] = 1
动态规划的填表顺序
mask|(1<<next) 一定大于mask,更按mask从小到处理可以保证无后效性。
动态规划的返回值
dp.back() 之和
关于去重
一个数出现n次,则除以n! 。就是n的排列数。注意:12! 在int32表示范围中。
变量解释
setQ 记录小于等于2e9的
代码
核心代码
class Solution { public: int numSquarefulPerms(vector<int>& nums) { m_c = nums.size(); m_iMaskCount = 1 << m_c; unordered_set<int> setQ; for (int i = 0; i * i <= 2'000'000'000; i++) { setQ.emplace(i * i); } vector<vector<int>> vNext(m_c); for (int i = 0; i < m_c; i++) { for (int j = 0; j < i; j++) { if (setQ.count(nums[i] + nums[j])) { vNext[i].emplace_back(j); vNext[j].emplace_back(i); } } } vector<vector<int>> dp(m_iMaskCount, vector<int>(m_c)); for (int i = 0; i < m_c; i++) { dp[1 << i][i] = 1; } for (int mask = 0; mask < m_iMaskCount; mask++) { for (int end = 0; end < m_c; end++) { for (const auto& next : vNext[end]) { if (mask & (1 << next)) {// next已经处理 continue; } dp[mask | (1 << next)][next] += dp[mask][end]; } } } int iCnt = std::accumulate(dp.back().begin(), dp.back().end(), 0); std::unordered_map<int, int> mNumCount; for (const auto& n : nums) { mNumCount[n]++; } for (const auto& [tmp, cnt] : mNumCount) { for (int j = 1; j <= cnt; j++) { iCnt /= j; } } return iCnt; } int m_c,m_iMaskCount; };
测试用例
template<class T> void Assert(const T& t1, const T& t2) { assert(t1 == t2); } template<class T> void Assert(const vector<T>& v1, const vector<T>& v2) { if (v1.size() != v2.size()) { assert(false); return; } for (int i = 0; i < v1.size(); i++) { Assert(v1[i], v2[i]); } } int main() { vector<int> nums; { Solution sln; nums = { 0,0,1 }; auto res = sln.numSquarefulPerms(nums); Assert(res, 3); } { Solution sln; nums = { 0,0,0,1,1,1 }; auto res = sln.numSquarefulPerms(nums); Assert(res, 4); } { Solution sln; nums = { 1,17,8 }; auto res = sln.numSquarefulPerms(nums); Assert(res, 2); } { Solution sln; nums = { 2,2,2 }; auto res = sln.numSquarefulPerms(nums); Assert(res, 1); } }