作者推荐
【动态规划】【前缀和】【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
动态规划
动态规划的状态表示
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);
}
}
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快
速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
相关下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653
我想对大家说的话 |
---|
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。