【动态规划】【C++算法】LeetCoce996正方形数组的数目

简介: 【动态规划】【C++算法】LeetCoce996正方形数组的数目

作者推荐

动态规划】【前缀和】【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);
  }
  
  
}


相关文章
|
1月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
49 2
|
2月前
|
算法
动态规划算法学习三:0-1背包问题
这篇文章是关于0-1背包问题的动态规划算法详解,包括问题描述、解决步骤、最优子结构性质、状态表示和递推方程、算法设计与分析、计算最优值、算法实现以及对算法缺点的思考。
99 2
动态规划算法学习三:0-1背包问题
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
47 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
2月前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
79 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
2月前
|
算法
动态规划算法学习二:最长公共子序列
这篇文章介绍了如何使用动态规划算法解决最长公共子序列(LCS)问题,包括问题描述、最优子结构性质、状态表示、状态递归方程、计算最优值的方法,以及具体的代码实现。
167 0
动态规划算法学习二:最长公共子序列
|
2月前
|
存储 算法 C++
高精度算法(加、减、乘、除,使用c++实现)
高精度算法(加、减、乘、除,使用c++实现)
666 0
高精度算法(加、减、乘、除,使用c++实现)
|
2月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
26 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
2月前
|
算法 数据处理 C++
c++ STL划分算法;partition()、partition_copy()、stable_partition()、partition_point()详解
这些算法是C++ STL中处理和组织数据的强大工具,能够高效地实现复杂的数据处理逻辑。理解它们的差异和应用场景,将有助于编写更加高效和清晰的C++代码。
43 0
|
2月前
|
存储 算法
动态规划算法学习一:DP的重要知识点、矩阵连乘算法
这篇文章是关于动态规划算法中矩阵连乘问题的详解,包括问题描述、最优子结构、重叠子问题、递归方法、备忘录方法和动态规划算法设计的步骤。
156 0
|
2月前
|
存储 算法 决策智能
【算法】博弈论(C/C++)
【算法】博弈论(C/C++)
下一篇
DataWorks