【C++刷题】优选算法——动态规划第三辑

2024-04-12 08:12

本文主要是介绍【C++刷题】优选算法——动态规划第三辑,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  1. 最大子数组和
状态表示:dp[i]: 表示以i位置元素结尾的所有子数组中的最大和
状态转移方程:dp[i] = max(nums[i], dp[i-1] + nums[i])
int maxSubArray(vector<int>& nums)
{// 1.dp数组vector<int> dp(nums.size());// 2.初始化dp[0] = nums[0];// 3.状态转移方程for(int i = 1; i < dp.size(); ++i){dp[i] = max(nums[i], dp[i-1] + nums[i]);}// 4.返回值int ret = dp[0];for(int i = 1; i < dp.size(); ++i)if(dp[i] > ret) ret = dp[i];return ret;
}
  1. 环形子数组的最大和
通过分类讨论,将环形问题可以转化为线性问题:
第一种情况:最大子数组和处于中间位置
第二种情况:最大子数组和处于两头位置
状态表示:dp_max[i]: 表示以i位置元素结尾的所有子数组中的最大和dp_min[i]: 表示以i位置元素结尾的所有子数组中的最小和
状态转移方程:dp_max[i] = max(nums[i], dp_max[i-1] + nums[i])dp_min[i] = min(nums[i], dp_min[i-1] + nums[i])
int maxSubarraySumCircular(vector<int>& nums)
{// 1.dp数组vector<int> dp_max(nums.size());vector<int> dp_min(nums.size());// 2.初始化dp_max[0] = dp_min[0] = nums[0];// 3.状态转移方程for(int i = 1; i < nums.size(); ++i){dp_max[i] = max(nums[i], dp_max[i-1] + nums[i]);dp_min[i] = min(nums[i], dp_min[i-1] + nums[i]);}// 4.返回值int sum = 0;for(int i = 0; i < nums.size(); ++i){sum += nums[i];}int max = dp_max[0];int min = dp_min[0];for(int i = 1; i < nums.size(); ++i){if(dp_max[i] > max) max = dp_max[i];if(dp_min[i] < min) min = dp_min[i];}// if(sum - min == 0) return max;// return max > sum - min ? max : sum - min;return sum == min ? max : std::max(max, sum - min);
}
  1. 乘积最大子数组
状态表示:f[i]: 表示以i位置元素结尾的所有子数组中最大积的值g[i]: 表示以i位置元素结尾的所有子数组中最小积的值
状态转移方程:f[i] = max(nums[i], max(f[i-1] * nums[i], g[i-1] * nums[i]));g[i] = min(nums[i], min(f[i-1] * nums[i], g[i-1] * nums[i]));
int maxProduct(vector<int>& nums)
{// 1.dp数组vector<int> f(nums.size());vector<int> g(nums.size());// 2.初始化f[0] = g[0] = nums[0];// 3.状态转移方程for(int i = 1; i < nums.size(); ++i){f[i] = max(nums[i], max(f[i-1] * nums[i], g[i-1] * nums[i]));g[i] = min(nums[i], min(f[i-1] * nums[i], g[i-1] * nums[i]));}// 4.返回值int ret = f[0];for(int i = 1; i < f.size(); ++i){if(f[i] > ret) ret = f[i];}return ret;
}
  1. 乘积为正数的最长子数组长度
状态表示:f[i]: 表示以i位置元素结尾的所有子数组中乘积为正的最大的子数组长度g[i]: 表示以i位置元素结尾的所有子数组中乘积为负的最大的子数组长度
int getMaxLen(vector<int>& nums)
{// 1.dp数组vector<int> f(nums.size());vector<int> g(nums.size());// 2.初始化f[0] = (nums[0] > 0 ? 1 : 0);g[0] = (nums[0] < 0 ? 1 : 0);// 3.状态转移方程for(int i = 1; i < nums.size(); ++i){if(nums[i] > 0) {f[i] = f[i-1] + 1;g[i] = g[i-1] == 0 ? 0 : g[i-1] + 1;}else if(nums[i] < 0){f[i] = g[i-1] == 0 ? 0 : g[i-1] + 1;g[i] = f[i-1] + 1;}}// 4. 返回值int ret = f[0];for(int i = 1; i < f.size(); ++i){if(f[i] > ret) ret = f[i];}return ret;
}
  1. 等差数列划分
状态表示:dp[i]: 表示以i位置元素结尾的所有子数组中等差数列的个数
状态转移方程:nums[i] - nums[i-1] == nums[i-1] - nums[i-2] 情况下: dp[i] = dp[i-1] + 1;否则: dp[i] = 0;
int numberOfArithmeticSlices(vector<int>& nums)
{// 0.边界情况处理if(nums.size() < 3) return 0;// 1.dp数组vector<int> dp(nums.size());// 2.初始化dp[0] = dp[1] = 0;// 3.状态转移方程for(int i = 2; i < dp.size(); ++i){if(nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]){dp[i] = dp[i-1] + 1;}}// 4.返回值int ret = 0;for(int i = 0; i < dp.size(); ++i){ret += dp[i];}return ret;
}
  1. 最长湍流子数组
状态表示:f[i]: 表示以i位置元素为结尾的所有子数组中,最后呈现"上升"状态的最长湍流数组的长度g[i]: 表示以i位置元素为结尾的所有子数组中,最后呈现"下降"状态的最长湍流数组的长度
状态转移方程:if(arr[i-1] > arr[i]) f[i] = g[i-1] + 1;else f[i] = 1;if(arr[i-1] < arr[i]) g[i] = f[i-1] + 1;else g[i] = 1;
int maxTurbulenceSize(vector<int>& arr)
{// 1.dp数组vector<int> f(arr.size());vector<int> g(arr.size());// 2.初始化f[0] = g[0] = 1;// 3.状态转移方程for(int i = 1; i < arr.size(); ++i){if(arr[i-1] > arr[i]){f[i] = g[i-1] + 1;}else{f[i] = 1;}if(arr[i-1] < arr[i]){g[i] = f[i-1] + 1;}else{g[i] = 1;}}// 4.返回值int ret = 0;for(int i = 0; i < arr.size(); ++i){if(f[i] > ret) ret = f[i];if(g[i] > ret) ret = g[i];}return ret;
}
  1. 单词拆分
状态表示:dp[i]: 表示[0, i]区间内的字符串,能否被字典中的单词拼接而成
状态转移方程:dp[i] = dp[j-1] == true && s(j ~ i) in the wordDict
bool Find(string s, vector<string>& wordDict)
{for(auto &e : wordDict){if(s == e) return true;}return false;
}
bool wordBreak(string s, vector<string>& wordDict)
{// 1.dp数组vector<bool> dp(s.size());// 2.初始化dp[0] = Find(s.substr(0, 1), wordDict);// 3.状态转移方程for(int i = 1; i < dp.size(); ++i){bool flag = false;            int j = i;for(; j >= 0; --j){if(dp[j] && Find(s.substr(j + 1, i - j), wordDict)){flag = true;break;}}if(j < 0 && Find(s.substr(0, i + 1), wordDict)) flag = true;dp[i] = flag;}// 4.返回值return dp.back();
}
  1. 环绕字符串中唯一的子字符串
状态表示:dp[i]: 表示以i位置元素为结尾的所有的子串中,在base中出现的次数
状态转移方程:s[i-1] + 1 == s[i] || (s[i-1] == 'z' && s[i] == 'a') -> dp[i] = dp[i-1] + 1;else -> dp[i] = 1;
int findSubstringInWraproundString(string s)
{// 1.dp数组vector<int> dp(s.size());// 2.初始化dp[0] = 1;// 3.状态转移方程for(int i = 1; i < dp.size(); ++i){if(s[i-1] + 1 == s[i] || (s[i-1] == 'z' && s[i] == 'a')){dp[i] = dp[i-1] + 1;}else{dp[i] = 1;}}// 4.返回值vector<int> v(26);for(int i = 0; i < dp.size(); ++i){v[s[i] - 'a'] = max(dp[i], v[s[i] - 'a']);}int sum = 0;for(int i = 0; i < 26; ++i){sum += v[i];}return sum;
}
  1. 最长递增子序列
状态表示:dp[i]: 表示以i位置元素为结尾的所有子序列中,最长递增子序列的长度
状态转移方程:nums[j] < nums[i]: dp[i] = max(dp[i], dp[j] + 1);
int lengthOfLIS(vector<int>& nums)
{// 1.dp数组vector<int> dp(nums.size(), 1);// 2.初始化// 3.状态转移方程for(int i = 1; i < dp.size(); ++i){for(int j = i - 1; j >= 0; --j){if(nums[i] > nums[j]){dp[i] = max(dp[i], dp[j] + 1);}}}// 4.返回值int ret = 0;for(int i = 0; i < dp.size(); ++i){ret = max(ret, dp[i]);}return ret;
}
  1. 摆动序列
状态表示:dp[i]: 表示以i位置元素为结尾的所有子序列中,最长摆动序列的长度继续细化:f[i]: 表示以i位置元素为结尾的所有子序列中,最后一个位置呈现"上升"趋势的,最长摆动序列的长度g[i]: 表示以i位置元素为结尾的所有子序列中,最后一个位置呈现"下降"趋势的,最长摆动序列的长度
状态转移方程:nums[j] < nums[i]: f[i] = max(g[j] + 1, f[i]);nums[j] < nums[i]: g[i] = max(f[j] + 1, g[i]);
int wiggleMaxLength(vector<int>& nums)
{// 1.dp数组vector<int> f(nums.size(), 1);vector<int> g(nums.size(), 1);// 2.初始化// f[0] = g[0] = 1;// 3.状态转移方程for(int i = 1; i < nums.size(); ++i){for(int j = i - 1; j >= 0; --j){if(nums[j] < nums[i]){f[i] = max(g[j] + 1, f[i]);}else if(nums[j] > nums[i]){g[i] = max(f[j] + 1, g[i]);}}}// 4.返回值int f_max = 0, g_max = 0;for(int i = 0; i < nums.size(); ++i){f_max = max(f_max, f[i]);g_max = max(g_max, g[i]);}return max(f_max, g_max);
}
  1. 最长递增子序列的个数
状态表示:len[i]: 表示以i位置元素为结尾的所有子序列中,最长递增子序列的“长度”count[i]: 表示以i位置元素为结尾的所有子序列中,最长递增子序列的“个数”
状态转移方程:nums[j] < nums[i]:len[j] + 1 == len[i]:cnt += count[j];len[j] + 1 > len[i]:len[i] = len[j] + 1;cnt = count[j];
int findNumberOfLIS(vector<int>& nums)
{// 1.dp数组vector<int> len(nums.size(), 1);vector<int> count(nums.size(), 1);// 2.初始化// len[0] = 1;// count[0] = 1;// 3.状态转移方程for(int i = 1; i < nums.size(); ++i){int cnt = 1;for(int j = i - 1; j >= 0; --j){if(nums[j] < nums[i]){if(len[j] + 1 == len[i]) cnt += count[j];else if(len[j] + 1 > len[i]){len[i] = len[j] + 1;cnt = count[j];}}}count[i] = cnt;}// 4.返回值int max_len = 1;int cnt = 0;for(int i = 0; i < nums.size(); ++i){if(len[i] == max_len) cnt += count[i];else if(len[i] > max_len){max_len = len[i];cnt = count[i];}}return cnt;
}
  1. 最长数对链
状态表示:dp[i]表示: 以i位置元素为结尾的所有数对链中,最长的数对链的长度
状态转移方程:pairs[j][1] < pairs[i][0]:dp[i] = max(dp[i], dp[j] + 1);
int findLongestChain(vector<vector<int>>& pairs)
{// 0.预处理sort(pairs.begin(), pairs.end());// 1.dp数组vector<int> dp(pairs.size(), 1);// 2.初始化// dp[0] = 1;// 3.状态转移方程for(int i = 1; i < dp.size(); ++i){for(int j = i - 1; j >= 0; --j){if(pairs[j][1] < pairs[i][0]){dp[i] = max(dp[i], dp[j] + 1);}}}// 4.返回值int ret = dp[0];for(int i = 1; i < dp.size(); ++i){ret = max(ret, dp[i]);}return ret;
}
  1. 最长定差子序列
状态表示:dp[i]: 表示以i位置元素为结尾的所有子序列中,最长的等差子序列的长度
状态转移方程:hash[arr[i]] = hash[arr[i] - difference] + 1;
int longestSubsequence(vector<int>& arr, int difference)
{// 1.dp数组unordered_map<int, int> hash; // <arr[i], dp[i]>// 2.初始化hash[arr[0]] = 1;// 3.状态转移方程for(int i = 1; i < arr.size(); ++i){if(hash.count(arr[i] - difference)){hash[arr[i]] = hash[arr[i] - difference] + 1;}else{hash[arr[i]] = 1;}}// 4.返回值int ret = 0;for(auto &e : hash){ret = max(ret, e.second);}return ret;
}
  1. 最长的斐波那契子序列的长度
状态表示:dp[i][j]: 表示以i位置元素以及j位置元素为结尾的所有子序列中,最长的斐波那契子序列的长度 (i < j)
状态转移方程:vv[i][j] = vv[hash[arr[j] - arr[i]]][i] + 1;
int lenLongestFibSubseq(vector<int>& arr)
{unordered_map<int, int> hash; // <arr[i], i>for(int i = 0; i < arr.size(); ++i){hash[arr[i]] = i;}// 1.dp数组vector<vector<int>> vv(arr.size(), vector<int>(arr.size(), 2));// 2.初始化// 3.状态转移方程   for(int j = 2; j < vv[0].size(); ++j){for(int i = 1; i < j; ++i){if(hash.count(arr[j] - arr[i]) && hash[arr[j] - arr[i]] < i){vv[i][j] = vv[hash[arr[j] - arr[i]]][i] + 1;}}}// 4.返回值int ret = 2;for(int j = 2; j < vv[0].size(); ++j){for(int i = 1; i < j; ++i){ret = max(ret, vv[i][j]);}}return ret < 3 ? 0 : ret;
}
  1. 最长等差数列
状态表示:dp[i][j]: 表示以i位置元素以及j位置元素为结尾的所有子序列中,最长的等差子序列的长度
状态转移方程:hash.count(elem): dp[i][j] = dp[hash[elem]][i] + 1;
int longestArithSeqLength(vector<int>& nums)
{if(nums.size() == 2) return 2;// 0.优化unordered_map<int, int> hash; // <nums[i], i>hash[nums[0]] = 0;// 1.dp数组vector<vector<int>> dp(nums.size(), vector<int>(nums.size(), 2));// 2.初始化// 3.状态转移方程for(int i = 1; i < nums.size() - 1; ++i){for(int j = i + 1; j < nums.size(); ++j){int a = 2 * nums[i] - nums[j];if(hash.count(a)){dp[i][j] = dp[hash[a]][i] + 1; }}hash[nums[i]] = i;}// 4.返回值int ret = 0;for(int i = 1; i < nums.size() - 1; ++i){for(int j = i + 1; j < nums.size(); ++j){ret = max(ret, dp[i][j]);}}return ret;
}
  1. 等差数列划分 II - 子序列
状态表示:dp[i][j]: 表示以i位置元素以及j位置元素为结尾的所有子序列中,所有等差子序列的个数
状态转移方程:nums[k] == (long long)2 * nums[i] - nums[j]: dp[i][j] += (dp[k][i] + 1);
int numberOfArithmeticSlices(vector<int>& nums)
{// 1.dp数组vector<vector<int>> dp(nums.size(), vector<int>(nums.size()));// 2.初始化// 3.状态转移方程for(int i = 1; i < nums.size() - 1; ++i){for(int j = i + 1; j < nums.size(); ++j){for(int k = i - 1; k >= 0; --k){if(nums[k] == (long long)2 * nums[i] - nums[j]){dp[i][j] += (dp[k][i] + 1);}}}}// 4.返回值int ret = 0;for(int i = 1; i < nums.size() - 1; ++i){for(int j = i + 1; j < nums.size(); ++j){ret += dp[i][j];}}return ret;
}

这篇关于【C++刷题】优选算法——动态规划第三辑的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/896583

相关文章

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

详解C++中类的大小决定因数

《详解C++中类的大小决定因数》类的大小受多个因素影响,主要包括成员变量、对齐方式、继承关系、虚函数表等,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录1. 非静态数据成员示例:2. 数据对齐(Padding)示例:3. 虚函数(vtable 指针)示例:4. 继承普通继承虚继承5.

C++中std::distance使用方法示例

《C++中std::distance使用方法示例》std::distance是C++标准库中的一个函数,用于计算两个迭代器之间的距离,本文主要介绍了C++中std::distance使用方法示例,具... 目录语法使用方式解释示例输出:其他说明:总结std::distance&n编程bsp;是 C++ 标准

C#如何动态创建Label,及动态label事件

《C#如何动态创建Label,及动态label事件》:本文主要介绍C#如何动态创建Label,及动态label事件,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#如何动态创建Label,及动态label事件第一点:switch中的生成我们的label事件接着,

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.