【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

相关文章

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

一文详解SpringBoot中控制器的动态注册与卸载

《一文详解SpringBoot中控制器的动态注册与卸载》在项目开发中,通过动态注册和卸载控制器功能,可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,下面我们就来看看Sp... 目录项目结构1. 创建 Spring Boot 启动类2. 创建一个测试控制器3. 创建动态控制器注