【状态机动态规划】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++**实现。

相关推荐
liulilittle2 小时前
OPENPPP2 网络驱动模式
开发语言·网络·c++·网络协议·信息与通信·通信
mjhcsp2 小时前
C++ AC 自动机:原理、实现与应用全解析
java·开发语言·c++·ac 自动机
寻星探路2 小时前
【算法通关】双指针技巧深度解析:从基础到巅峰(Java 最优解)
java·开发语言·人工智能·python·算法·ai·指针
wen__xvn2 小时前
力扣第 484 场周赛
算法·leetcode·职场和发展
YuTaoShao2 小时前
【LeetCode 每日一题】865. 具有所有最深节点的最小子树——(解法一)自顶向下
算法·leetcode·职场和发展
爱吃生蚝的于勒2 小时前
【Linux】进程间通信之匿名管道
linux·运维·服务器·c语言·数据结构·c++·vim
wanderist.2 小时前
C++输入输出的一些问题
开发语言·c++·图论
金色熊族2 小时前
MV结构下设置Qt表格的代理(2)
c++·qt
寻星探路2 小时前
【算法专题】哈希表:从“两数之和”到“最长连续序列”的深度解析
java·数据结构·人工智能·python·算法·ai·散列表