【动态规划 状态机dp 性能优化】3098. 求出所有子序列的能量和

这篇具有很好参考价值的文章主要介绍了【动态规划 状态机dp 性能优化】3098. 求出所有子序列的能量和。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文涉及知识点

动态规划 状态机dp 性能优化

LeetCode3098. 求出所有子序列的能量和

给你一个长度为 n 的整数数组 nums 和一个 正 整数 k 。
一个子序列的 能量 定义为子序列中 任意 两个元素的差值绝对值的 最小值 。
请你返回 nums 中长度 等于 k 的 所有 子序列的 能量和 。
由于答案可能会很大,将答案对 109 + 7 取余 后返回。
示例 1:
输入:nums = [1,2,3,4], k = 3
输出:4
解释:
nums 中总共有 4 个长度为 3 的子序列:[1,2,3] ,[1,3,4] ,[1,2,4] 和 [2,3,4] 。能量和为 |2 - 3| + |3 - 4| + |2 - 1| + |3 - 4| = 4 。
示例 2:
输入:nums = [2,2], k = 2
输出:0
解释:
nums 中唯一一个长度为 2 的子序列是 [2,2] 。能量和为 |2 - 2| = 0 。
示例 3:
输入:nums = [4,3,-1], k = 2
输出:10
解释:
nums 总共有 3 个长度为 2 的子序列:[4,3] ,[4,-1] 和 [3,-1] 。能量和为 |4 - 3| + |4 - (-1)| + |3 - (-1)| = 10 。

提示:
2 <= n == nums.length <= 50
-108 <= nums[i] <= 108
2 <= k <= n

动态规划(状态机dp)初版

动态规划的状态 表示

pre 表示已经处理完前x个数组符合条件的数量,dp表示已经处理完x+1数组符合条件的数量。

pre[i][j][end][len] 表示此子序列:
a,长度为len。
b,以nums[end]结束。
c,nums[j]-nums[i]的差最小。如果多个(i,j)符合条件,取最小的。比如:{1,2,3}的(I,j)是{0,1}而不是{1,2}。
空间复杂度:O(nnnk)
dp类似。

动态规划的转移方程

只需要从x 推导x+1,不需要推导x+2,x+3 ⋯ \cdots ,如果硬要的话需要用前缀和(后缀和)。 
{ d p = p r e 不选择 n u m s [ x ] d p [ i ] [ j ] [ x ] [ l e n + 1 ] + = . . . e l s e 且 n u m s [ j ] − n u m s [ i ] < = n u m s [ x ] − n u m s [ e n d ] d p [ e n d ] [ x ] [ x ] [ l e n + 1 ] + = . . . e l s e \begin{cases} dp = pre && 不选择nums[x] \\ dp[i][j][x][len+1] += ... && else 且 nums[j]-nums[i] <= nums[x]-nums[end] \\ dp[end][x][x][len+1] += ... else \\ \end{cases} dp=predp[i][j][x][len+1]+=...dp[end][x][x][len+1]+=...else不选择nums[x]elsenums[j]nums[i]<=nums[x]nums[end]
时间复杂度:O(nnnkn) 估计超时
剪枝:
枚举的时候确保 i < j ,且 j <= x。

动态规划+前缀和

拆分成若干个子问题,假定序列存在(i,j),且此序列的能力为power = nums[j]-nums[i]。

动态规划的状态表示

dp[len][end] 表示 子序列的长度为len,最后一个元素是end。
空间复杂度:O(kn)

利用前缀和优化 动态规划的转移方程

枚举end,end not ∈ \in (i,j) ,否则此序列的能量就不是nums[j]-nums[i]了。
{ o l d E n d ∈ [ 0 , e n d ) 且 n u m s [ e n d ] − n u m s [ o l d E n d ] > p o w e r e n d < = i o e d E n d ∈ ( e n d , n ) 且 n u m s [ e n d ] − n u m s [ o l d E n d ] > = p o w e r e n d > = j \begin{cases} oldEnd \in [0,end)且nums[end] -nums[oldEnd] > power && end <= i \\ oedEnd \in (end,n) 且 nums[end] -nums[oldEnd] >= power && end >=j \\ \end{cases} {oldEnd[0,end)nums[end]nums[oldEnd]>poweroedEnd(end,n)nums[end]nums[oldEnd]>=powerend<=iend>=j

如果不利用前缀和优先,时间复杂度:O(knn),利用前缀和优化O(kn)。
总时间复杂度:O(knkn)。

动态规划的初始状态

枚举所有长度为2

动态规划的填表顺序

l e n = 3 n _{len=3}^{n} len=3n

动态规划的返回值

len == k 且 end >=j 才是需要统计的子序列数量。

代码

没用前缀和优化

理论上过不了,实际过了。

template<int MOD = 1000000007>
class C1097Int
{
public:
	C1097Int(long long llData = 0) :m_iData(llData% MOD)
	{

	}
	C1097Int  operator+(const C1097Int& o)const
	{
		return C1097Int(((long long)m_iData + o.m_iData) % MOD);
	}
	C1097Int& operator+=(const C1097Int& o)
	{
		m_iData = ((long long)m_iData + o.m_iData) % MOD;
		return *this;
	}
	C1097Int& operator-=(const C1097Int& o)
	{
		m_iData = (m_iData + MOD - o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator-(const C1097Int& o)
	{
		return C1097Int((m_iData + MOD - o.m_iData) % MOD);
	}
	C1097Int  operator*(const C1097Int& o)const
	{
		return((long long)m_iData * o.m_iData) % MOD;
	}
	C1097Int& operator*=(const C1097Int& o)
	{
		m_iData = ((long long)m_iData * o.m_iData) % MOD;
		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(long long n)const
	{
		C1097Int iRet = 1, iCur = *this;
		while (n)
		{
			if (n & 1)
			{
				iRet *= iCur;
			}
			iCur *= iCur;
			n >>= 1;
		}
		return iRet;
	}
	C1097Int PowNegative1()const
	{
		return pow(MOD - 2);
	}
	int ToInt()const
	{
		return m_iData;
	}
private:
	int m_iData = 0;;
};

class Solution {
public:
	int sumOfPowers(vector<int>& nums, const int K) {
		m_c = nums.size();
		sort(nums.begin(), nums.end());		
		C1097Int<> biRet = 0;
		for (int i = 0; i < m_c; i++) {
			for (int j = i + 1; j < m_c; j++) {
				auto cur = Do(nums, i, j, K);
				biRet += cur;
				//std::cout << " i :" << i << " j:" << j << " " << cur.ToInt() <<  std::endl;
			}
		}
		return biRet.ToInt();
	}
	C1097Int<> Do(const vector<int>& nums,int i,int j, const int K) {
		const int iDiff = nums[j] - nums[i];
		vector<vector<C1097Int<>>> dp(K + 1, vector<C1097Int<>>(m_c));
		for (int end = 0; end <= i; end++) {
			for (int end1 = 0; end1 < end; end1++) {
				if (nums[end] - nums[end1] > iDiff) {
					dp[2][end] += 1;
				}
			}
		}
		dp[2][j] = 1;
	
		for (int len = 3; len <= K; len++) {
			for (int end = 0; end <= i; end++) {
				for (int end1 = 0; end1 < end; end1++) {
					if (nums[end] - nums[end1] > iDiff) {
						dp[len][end] += dp[len - 1][end1];
					}
				}
			}
			dp[len][j] = dp[len - 1][i];
			for (int end = j+1; end < m_c; end++) {
				for (int end1 = j; end1 < end; end1++) {
					if (nums[end] - nums[end1] >= iDiff) {
						dp[len][end] += dp[len - 1][end1];
					}
				}
			}
		}
		return std::accumulate(dp.back().begin() + j, dp.back().end(), C1097Int<>())*iDiff;
	}
	int m_c;
};

测试用例

int main()
{
	vector<int> nums;
	int k;
	
	{
		Solution sln;
		nums = { 6,14,4,13 }, k = 3;
		auto res = sln.sumOfPowers(nums, k);
		Assert(6, res);
	}
	{
		Solution sln;
		nums = { 1,2,3,4 }, k = 3;
		auto res = sln.sumOfPowers(nums, k);
		Assert(4, res);
	}
	{
		Solution sln;
		nums = { 4,3,-1 }, k = 2;
		auto res = sln.sumOfPowers(nums, k);
		Assert(10, res);
	}

	{
		Solution sln;
		nums = { 2,2 }, k = 2;
		auto res = sln.sumOfPowers(nums, k);
		Assert(0, res);
	}
	{
		Solution sln;
		nums = { 2,246006,496910,752786,1013762,1279948,1551454,1828436,2110982,2399316,2693558,2993942,3300640,3613766,3933442,4259696,4592656,4932556,5279494,5633522,5994678,6363102,6739028,7122528,7513792,7913044,8320394,8736004,9160062,9592750,10034184,10484602,10944108,11412852,11891048,12378822,12876346,13383746,13901098,14428528,14966126,15514010,16072380,16641300,17220904,17811360,18412850,19025600,19649778,20285440 }, k = 37;
		auto res = sln.sumOfPowers(nums, k);
		Assert(273504325, res);
	}
}

利用前缀和优化:用时减少不到50%

template<int MOD = 1000000007>
class C1097Int
{
public:
	C1097Int(long long llData = 0) :m_iData(llData% MOD)
	{

	}
	C1097Int  operator+(const C1097Int& o)const
	{
		return C1097Int(((long long)m_iData + o.m_iData) % MOD);
	}
	C1097Int& operator+=(const C1097Int& o)
	{
		m_iData = ((long long)m_iData + o.m_iData) % MOD;
		return *this;
	}
	C1097Int& operator-=(const C1097Int& o)
	{
		m_iData = (m_iData + MOD - o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator-(const C1097Int& o)
	{
		return C1097Int((m_iData + MOD - o.m_iData) % MOD);
	}
	C1097Int  operator*(const C1097Int& o)const
	{
		return((long long)m_iData * o.m_iData) % MOD;
	}
	C1097Int& operator*=(const C1097Int& o)
	{
		m_iData = ((long long)m_iData * o.m_iData) % MOD;
		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(long long n)const
	{
		C1097Int iRet = 1, iCur = *this;
		while (n)
		{
			if (n & 1)
			{
				iRet *= iCur;
			}
			iCur *= iCur;
			n >>= 1;
		}
		return iRet;
	}
	C1097Int PowNegative1()const
	{
		return pow(MOD - 2);
	}
	int ToInt()const
	{
		return m_iData;
	}
private:
	int m_iData = 0;;
};

class Solution {
public:
	int sumOfPowers(vector<int>& nums, const int K) {
		m_c = nums.size();
		sort(nums.begin(), nums.end());
		C1097Int<> biRet = 0;
		for (int i = 0; i < m_c; i++) {
			for (int j = i + 1; j < m_c; j++) {
				auto cur = Do(nums, i, j, K);
				biRet += cur;
				//std::cout << " i :" << i << " j:" << j << " " << cur.ToInt() <<  std::endl;
			}
		}
		return biRet.ToInt();
	}
	C1097Int<> Do(const vector<int>& nums, int i, int j, const int K) {
		const int iDiff = nums[j] - nums[i];
		vector<vector<C1097Int<>>> dp(K + 1, vector<C1097Int<>>(m_c));
		for (int end = 0; end <= i; end++) {
			for (int end1 = 0; end1 < end; end1++) {
				if (nums[end] - nums[end1] > iDiff) {
					dp[2][end] += 1;
				}
			}
		}
		dp[2][j] = 1;

		for (int len = 3; len <= K; len++) {
			int end1 = 0;
			C1097Int<> biRet = 0;
			for (int end = 0; end <= i; end++) {
				while ((end1 < end) && (nums[end] - nums[end1] > iDiff)) {
					biRet += dp[len - 1][end1];
					end1++;
				}
				dp[len ][end] = biRet;
			}
			dp[len][j] = dp[len - 1][i];
			C1097Int<> biRet2 = 0;
			for (int end = j + 1,end1=j ; end < m_c; end++) {
				while ((end1 < end) && (nums[end] - nums[end1] >= iDiff)) {
					biRet2 += dp[len - 1][end1];
					end1++;
				}
				dp[len][end] = biRet2;
			}
		}
		return std::accumulate(dp.back().begin() + j, dp.back().end(), C1097Int<>()) * iDiff;
	}
	int m_c;
};

【动态规划 状态机dp 性能优化】3098. 求出所有子序列的能量和,# 困难算法题,数据结构与算法,动态规划,算法,力扣,c++,状态机dp,前缀和,子序列

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++**实现。

【动态规划 状态机dp 性能优化】3098. 求出所有子序列的能量和,# 困难算法题,数据结构与算法,动态规划,算法,力扣,c++,状态机dp,前缀和,子序列文章来源地址https://www.toymoban.com/news/detail-859919.html

到了这里,关于【动态规划 状态机dp 性能优化】3098. 求出所有子序列的能量和的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法沉淀 —— 动态规划篇(简单多状态dp问题上)

    几乎所有的动态规划问题大致可分为以下5个步骤,后续所有问题分析都将基于此 1.、状态表示:通常状态表示分为以下两种,其中更是第一种为主。 以i为结尾 ,dp[i] 表示什么,通常为代求问题(具体依题目而定) 以i为开始 ,dp[i]表示什么,通常为代求问题(具体依题目而

    2024年04月11日
    浏览(43)
  • C++ 动态规划 状态压缩DP 最短Hamilton路径

    给定一张 n 个点的带权无向图,点从 0∼n−1 标号,求起点 0 到终点 n−1 的最短 Hamilton 路径。 Hamilton 路径的定义是从 0 到 n−1 不重不漏地经过每个点恰好一次。 输入格式 第一行输入整数 n 。 接下来 n 行每行 n 个整数,其中第 i 行第 j 个整数表示点 i 到 j 的距离(记为 a[

    2024年02月19日
    浏览(40)
  • acwing算法基础之动态规划--数位统计DP、状态压缩DP、树形DP和记忆化搜索

    暂无。。。 暂无。。。 题目1 :求a~b中数字0、数字1、…、数字9出现的次数。 思路:先计算1~a中每位数字出现的次数,然后计算1~b-1中每位数字出现的次数,两个相减即是最终答案。 那么,如何计算1~a中每位数字出现的次数呢? 首先,将a的每一位存入向量num中,例如a=123

    2024年02月04日
    浏览(51)
  • 【动态规划】简单多状态dp问题(1)打家劫舍问题

    打家劫舍问题 传送门:面试题 17.16. 按摩师 题目: 1.1 题目解析 越难的dp问题,看示例只能起到了解题目的效果,一般推不出啥普遍的规律,所以接下来就是我们的算法原理,通过动归的思想去理解,才会豁然开朗! 1.2 算法原理 1.2.1 状态表示 我们需要通过经验 + 题目要求去

    2024年02月12日
    浏览(42)
  • 动态规划——矩阵优化DP 学习笔记

    前置知识:矩阵、矩阵乘法。 斐波那契数列 在斐波那契数列当中, (f_1 = f_2 = 1) , (f_i = f_{i - 1} + f_{i - 2}) ,求 (f_n) 。 而分析式子可以知道,求 (f_k) 仅与 (f_{k - 1}) 和 (f_{k - 2}) 有关; 所以我们设矩阵 (F_i = begin{bmatrix} f_{i - 1} f_{i - 2} end{bmatrix}) 。 设矩阵 (text{Ba

    2024年02月08日
    浏览(56)
  • 动态规划——斜率优化DP 学习笔记

    适用于求解最优解(最大、最小)问题。 可以将转移方程可以化为 (left[begin{array}{rl} 仅与 space i space 有关 是我们想要最大/最小化的 \\\\ 仅与 space j space 有关 是已知的 \\\\ 与 space i space 和 space j space 都有关 是两项相乘 end{array}right]) 三部分的, 都可以考虑用斜率优化

    2024年02月08日
    浏览(47)
  • 【动态规划专栏】专题三:简单多状态dp--------3.删除并获得点数

    本专栏内容为:算法学习专栏,分为优选算法专栏,贪心算法专栏,动态规划专栏以及递归,搜索与回溯算法专栏四部分。 通过本专栏的深入学习,你可以了解并掌握算法。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:动态规划专栏 🚚代码仓库:小小unicorn的代码仓库🚚

    2024年03月22日
    浏览(43)
  • 【状态机dp 动态规划】100290. 使矩阵满足条件的最少操作次数

    动态规划汇总 状态机dp 给你一个大小为 m x n 的二维矩形 grid 。每次 操作 中,你可以将 任一 格子的值修改为 任意 非负整数。完成所有操作后,你需要确保每个格子 grid[i][j] 的值满足: 如果下面相邻格子存在的话,它们的值相等,也就是 grid[i][j] == grid[i + 1][j](如果存在)

    2024年04月24日
    浏览(35)
  • AcWing算法学习笔记:动态规划(背包 + 线性dp + 区间dp + 计数dp + 状态压缩dp + 树形dp + 记忆化搜索)

    算法 复杂度 时间复杂度0(nm) 空间复杂度0(nv) 代码 算法 通过滚动数组对01背包朴素版进行空间上的优化 f[i] 与 f[i - 1]轮流交替 若体积从小到大进行遍历,当更新f[i, j]时,f[i - 1, j - vi] 已经在更新f[i, j - vi]时被更新了 因此体积需要从大到小进行遍历,当更新f[i, j]时,f[i - 1,

    2024年02月21日
    浏览(43)
  • 15.动态规划:数据结构优化DP

    数据结构优化DP有前缀和、滑动窗口、树状数组、线段树、单调栈、单调队列 中等 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如, [3,6,2,7] 是数组 [0,3,1,6,2,2

    2024年02月03日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包