本文主要是介绍【C++刷题】优选算法——动态规划第三辑,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
- 最大子数组和
状态表示: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;
}
- 环形子数组的最大和
通过分类讨论,将环形问题可以转化为线性问题:
第一种情况:最大子数组和处于中间位置
第二种情况:最大子数组和处于两头位置
状态表示: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);
}
- 乘积最大子数组
状态表示: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;
}
- 乘积为正数的最长子数组长度
状态表示: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;
}
- 等差数列划分
状态表示: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;
}
- 最长湍流子数组
状态表示: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;
}
- 单词拆分
状态表示: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();
}
- 环绕字符串中唯一的子字符串
状态表示: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;
}
- 最长递增子序列
状态表示: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;
}
- 摆动序列
状态表示: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);
}
- 最长递增子序列的个数
状态表示: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;
}
- 最长数对链
状态表示: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;
}
- 最长定差子序列
状态表示: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;
}
- 最长的斐波那契子序列的长度
状态表示: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;
}
- 最长等差数列
状态表示: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;
}
- 等差数列划分 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++刷题】优选算法——动态规划第三辑的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!