【状态机动态规划】3686. 稳定子序列的数量|1969

本文涉及知识点

C++动态规划

3686. 稳定子序列的数量

给你一个整数数组 nums。

如果一个 子序列 中 不存在连续三个 元素奇偶性相同(仅考虑该子序列内),则称该子序列为稳定子序列 。

请返回所有稳定子序列的数量。

由于结果可能非常大,请将答案对 109 + 7 取余数后返回。

子序列 是一个从数组中通过删除某些元素(或不删除任何元素),并保持剩余元素相对顺序不变的 非空 数组。

示例 1:

输入: nums = [1,3,5]

输出: 6

解释:

稳定子序列为:[1], [3], [5], [1, 3], [1, 5], 和 [3, 5]。

子序列 [1, 3, 5] 不稳定,因为它包含三个连续的奇数。因此答案是 6。

示例 2:

输入: nums = [2,3,4,2]

输出: 14

解释:

唯一一个不稳定子序列是 [2, 4, 2],因为它包含三个连续的偶数。

所有其他子序列都是稳定子序列。因此答案是 14。

提示:

1 <= nums.length <= 105

1 <= nums[i] <= 105

动态规划

动态规划的状态表示

dp[n][m1][m2],n表示已经处理了nums长度为n的前缀,m2表示此子序列最后一个数字是否是奇数,m1表示奇偶性相同的最长后缀。 0 ≤ m 1 < 3 , 0 ≤ m 2 < 2 0 \le m1 <3,0\le m2 <2 0≤m1<3,0≤m2<2。

利用滚动向量优化空间,pre=dp[n],cur=dp[n+1]。优化后,空间复杂度:O(1)。

动态规划的填报顺序

枚举前驱状态,和选择。选取当前数字,不选取当前数字。

动态规划的转移方程

处理不选取当前数字: cur =pre。

前驱状态m2 当前数是否奇数 后续状态
true true cur[m1+1][m2]
true false cur[1][0]
false true cur[1][1]
false false cur[m1+1][m2]

动态规划的初始值

pre[0][0]=1

动态规划的返回值

∑ p r e \sum pre ∑pre -1。排除空串。

代码

核心代码

cpp 复制代码
template<long long MOD = 1000000007,class T1 = int, class T2 = long long>
class C1097Int
{
public:
	C1097Int(T1 iData = 0) :m_iData(iData% MOD)
	{

	}
	C1097Int(T2 llData) :m_iData(llData% MOD) {

	}
	C1097Int  operator+(const C1097Int& o)const
	{
		return C1097Int(((T2)m_iData + o.m_iData) % MOD);
	}
	C1097Int& operator+=(const C1097Int& o)
	{
		m_iData = ((T2)m_iData + o.m_iData) % MOD;
		return *this;
	}
	C1097Int& operator-=(const C1097Int& o)
	{
		m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator-(const C1097Int& o)const
	{
		return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD);
	}
	C1097Int  operator*(const C1097Int& o)const
	{
		return((T2)m_iData * o.m_iData) % MOD;
	}
	C1097Int& operator*=(const C1097Int& o)
	{
		m_iData = ((T2)m_iData * o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator/(const C1097Int& o)const
	{
		return *this * o.PowNegative1();
	}
	C1097Int& operator/=(const C1097Int& o)
	{
		*this *= o.PowNegative1();
		return *this;
	}
	bool operator==(const C1097Int& o)const
	{
		return m_iData == o.m_iData;
	}
	bool operator<(const C1097Int& o)const
	{
		return m_iData < o.m_iData;
	}
	C1097Int pow(T2 n)const
	{
		C1097Int iRet = (T1)1, iCur = *this;
		while (n)
		{
			if (n & 1)
			{
				iRet *= iCur;
			}
			iCur *= iCur;
			n >>= 1;
		}
		return iRet;
	}
	C1097Int PowNegative1()const
	{
		return pow(MOD - 2);
	}
	T1 ToInt()const
	{
		return ((T2)m_iData + MOD) % MOD;
	}
private:
	T1 m_iData = 0;;
};


class Solution {
		public:
			int countStableSubsequences(vector<int>& nums) {
				vector<vector<C1097Int<>>> pre(3, vector<C1097Int<>>(2));
				pre[0][0] = 1;
				for (const auto& n : nums)
				{
					auto cur = pre;
					for (int i = 0;i < 3;i++) {
						for (int j = 0;j < 2; j++) {
							if (n & 1) {
								if (0 == j) {
									cur[1][1] += pre[i][j];
								}
								else {
									if (i < 2) {
										cur[i + 1][1] += pre[i][j];
									}
								}
							}
							else {
								if (1 == j) {
									cur[1][0] += pre[i][j];
								}
								else if (i < 2) {
									cur[i + 1][0] += pre[i][j];
								}
							}
						}
					}
					pre.swap(cur);
				}
				C1097Int<> ans(-1);
				for (const auto& v : pre) {
					ans += accumulate(v.begin(), v.end(), C1097Int<>(0));
				}
				return ans.ToInt();
			}
		};

单元测试

cpp 复制代码
	vector<int> nums;
	public:
		TEST_METHOD(TestMethod11)
		{
			nums = { 1, 3, 5 };	
			auto res = Solution().countStableSubsequences(nums);
			AssertEx(6, res);
		}
		TEST_METHOD(TestMethod12)
		{
			nums = { 2,3,4,2 };
			auto res = Solution().countStableSubsequences(nums);
			AssertEx(14, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法 用**C++**实现。

相关推荐
踩坑记录10 小时前
leetcode hot100 easy 101. 对称二叉树 递归 层序遍历 bfs
算法·leetcode·宽度优先
2501_9403152611 小时前
leetcode182动态口令(将字符的前几个元素放在字符串后面)
算法
安全二次方security²11 小时前
CUDA C++编程指南(7.25)——C++语言扩展之DPX
c++·人工智能·nvidia·cuda·dpx·cuda c++编程指南
老鼠只爱大米11 小时前
LeetCode经典算法面试题 #98:验证二叉搜索树(递归法、迭代法等五种实现方案详解)
算法·leetcode·二叉树·递归·二叉搜索树·迭代
爱装代码的小瓶子11 小时前
【c++与Linux基础】文件篇(4)虚拟文件系统VFS
linux·开发语言·c++
疯狂的喵16 小时前
C++编译期多态实现
开发语言·c++·算法
scx2013100417 小时前
20260129LCA总结
算法·深度优先·图论
2301_7657031417 小时前
C++中的协程编程
开发语言·c++·算法
m0_7487080517 小时前
实时数据压缩库
开发语言·c++·算法
小魏每天都学习17 小时前
【算法——c/c++]
c语言·c++·算法