作者推荐
【动态规划】【前缀和】【C++算法】LCP 57. 打地鼠
本文涉及知识点
动态规划汇总
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
动态规划
令 n = A.length
动态规划的状态表示
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
转移方程的复杂度为:O(n),故总时间复杂度为:O(2nnn)
动态规矩的初始状态
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);
}
}