Appearance
L7_02 复杂动态规划
一、二维动态规划
1.1 二维动态规划概述
二维动态规划使用二维数组来存储状态,适用于涉及两个维度的问题。
1.2 二维背包问题
cpp
int knapsack2D(vector<int>& weights, vector<int>& volumes,
vector<int>& values, int maxWeight, int maxVolume) {
int n = weights.size();
vector<vector<int>> dp(maxWeight + 1, vector<int>(maxVolume + 1, 0));
for (int i = 0; i < n; i++) {
for (int w = maxWeight; w >= weights[i]; w--) {
for (int v = maxVolume; v >= volumes[i]; v--) {
dp[w][v] = max(dp[w][v], dp[w - weights[i]][v - volumes[i]] + values[i]);
}
}
}
return dp[maxWeight][maxVolume];
}1.3 最长公共子序列(LCS)
cpp
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size(), n = text2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (text1[i - 1] == text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}二、动态规划最值优化
2.1 最大子矩阵和
cpp
int maxSubmatrixSum(vector<vector<int>>& matrix) {
int rows = matrix.size();
int cols = matrix[0].size();
int max_sum = INT_MIN;
for (int left = 0; left < cols; left++) {
vector<int> row_sums(rows, 0);
for (int right = left; right < cols; right++) {
for (int i = 0; i < rows; i++) {
row_sums[i] += matrix[i][right];
}
// 使用一维最大子数组和算法
int current_sum = 0, current_max = INT_MIN;
for (int sum : row_sums) {
current_sum = max(sum, current_sum + sum);
current_max = max(current_max, current_sum);
}
max_sum = max(max_sum, current_max);
}
}
return max_sum;
}2.2 最大正方形
cpp
int maximalSquare(vector<vector<char>>& matrix) {
int rows = matrix.size();
int cols = matrix[0].size();
vector<vector<int>> dp(rows, vector<int>(cols, 0));
int max_side = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (matrix[i][j] == '1') {
if (i == 0 || j == 0) {
dp[i][j] = 1;
} else {
dp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;
}
max_side = max(max_side, dp[i][j]);
}
}
}
return max_side * max_side;
}三、区间动态规划
3.1 区间DP概述
区间动态规划是一种特殊的动态规划,状态定义通常为 dp[i][j] 表示区间 [i, j] 的最优解。
3.2 石子合并问题
cpp
int stoneGame(vector<int>& stones) {
int n = stones.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
vector<int> prefix(n + 1, 0);
for (int i = 0; i < n; i++) {
prefix[i + 1] = prefix[i] + stones[i];
}
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len <= n; i++) {
int j = i + len - 1;
dp[i][j] = INT_MAX;
int sum = prefix[j + 1] - prefix[i];
for (int k = i; k < j; k++) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum);
}
}
}
return dp[0][n - 1];
}3.3 最长回文子序列
cpp
int longestPalindromeSubseq(string s) {
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int i = n - 1; i >= 0; i--) {
dp[i][i] = 1;
for (int j = i + 1; j < n; j++) {
if (s[i] == s[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}四、最长上升子序列(LIS)
4.1 O(n²) 解法
cpp
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, 1);
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
return *max_element(dp.begin(), dp.end());
}4.2 O(n log n) 解法
cpp
int lengthOfLIS(vector<int>& nums) {
vector<int> tails;
for (int num : nums) {
auto it = lower_bound(tails.begin(), tails.end(), num);
if (it == tails.end()) {
tails.push_back(num);
} else {
*it = num;
}
}
return tails.size();
}4.3 最长严格递增子序列
cpp
int lengthOfLISStrict(vector<int>& nums) {
vector<int> tails;
for (int num : nums) {
auto it = lower_bound(tails.begin(), tails.end(), num);
if (it == tails.end()) {
tails.push_back(num);
} else {
*it = num;
}
}
return tails.size();
}五、滚动数组优化
5.1 一维滚动数组
cpp
// 优化前
int fibonacci(int n) {
vector<int> dp(n + 1);
dp[0] = 0; dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
// 优化后
int fibonacci(int n) {
if (n <= 1) return n;
int prev2 = 0, prev1 = 1;
for (int i = 2; i <= n; i++) {
int curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}5.2 二维滚动数组
cpp
// 优化前
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m, vector<int>(n, 1));
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
// 优化后
int uniquePaths(int m, int n) {
vector<int> dp(n, 1);
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[j] += dp[j-1];
}
}
return dp[n-1];
}六、状态压缩动态规划
6.1 状态压缩概述
状态压缩DP使用二进制数来表示状态,适用于状态数量较少的问题。
6.2 旅行商问题(TSP)
cpp
int tsp(vector<vector<int>>& graph) {
int n = graph.size();
int full_mask = (1 << n) - 1;
vector<vector<int>> dp(n, vector<int>(1 << n, INT_MAX));
dp[0][1] = 0; // 从0出发
for (int mask = 1; mask <= full_mask; mask++) {
for (int u = 0; u < n; u++) {
if (!(mask & (1 << u))) continue;
for (int v = 0; v < n; v++) {
if (mask & (1 << v)) continue;
int new_mask = mask | (1 << v);
dp[v][new_mask] = min(dp[v][new_mask], dp[u][mask] + graph[u][v]);
}
}
}
int min_cost = INT_MAX;
for (int u = 1; u < n; u++) {
min_cost = min(min_cost, dp[u][full_mask] + graph[u][0]);
}
return min_cost;
}