本文最后更新于 2024年10月30日 下午
3-1
解: 采用动态规划算法。
定义子问题:
我们将原问题分割为更小的子问题:一个子序列中最长严格递增列的长度。
构建动态规划数组:
使用一个数组 d p dp d p 来存储以每个元素为结尾的最长递增子序列的长度。d p [ i ] dp[i] d p [ i ] 表示以 n u m s [ i ] nums[i] n u m s [ i ] 结尾的最长递增子序列的长度。初始时每个位置都设置为 1(每个元素自身可以作为一个长度为 1 的子序列)。
状态转移方程:
对于每个元素 n u m s [ i ] nums[i] n u m s [ i ] (从第二个元素开始),我们需要检查它之前的所有元素 n u m s [ j ] nums[j] n u m s [ j ] (j < i j < i j < i )。
如果 n u m s [ i ] > n u m s [ j ] nums[i] > nums[j] n u m s [ i ] > n u m s [ j ] ,则可以将 n u m s [ i ] nums[i] n u m s [ i ] 加入以 n u m s [ j ] nums[j] n u m s [ j ] 为结尾的子序列中,因此我们更新 d p [ i ] dp[i] d p [ i ] 为 d p [ j ] + 1 dp[j] + 1 d p [ j ] + 1 的最大值:
d p dp d p 数组中的最大值即为所求。
根据上述过程,算法的时间复杂度为O ( n 2 ) O(n^2) O ( n 2 )
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> #include <vector> using namespace std;class Solution {public : int lengthOfLIS (vector<int >& nums) { int n = nums.size (); if (n == 0 ) return 0 ; 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 ); int res = 1 ; for (int i = 0 ; i < n; i++) res = max (res, dp[i]); return res; } };int main () { vector<int > nums = {1 ,3 ,6 ,7 ,9 ,4 ,10 ,5 ,6 }; Solution sol; cout << sol.lengthOfLIS (nums) << endl; return 0 ; }
运行结果
3-3
解: 采用动态规划算法。
定义子问题:
将原问题分割为更小的子问题:计算出前 j j j 个单词的最小排版花费。
构建动态规划数组:
用数组 c [ j ] c[j] c [ j ] 表示从第 1 1 1 个单词到第 j j j 个单词的最小花费。构建矩阵 e x t r a s [ i ] [ j ] extras[i][j] e x t r a s [ i ] [ j ] 表示将单词 i i i 到 j j j 放在同一行时的剩余空格数;用矩阵 l c [ i ] [ j ] lc[i][j] l c [ i ] [ j ] 表示将单词 i i i 到 j j j 放在同一行时的空格花费(多余空格数的立方)。
状态转移方程:
对于每一个位置 j j j ,我们需要检查前面所有的可能断行位置 i i i ,并计算在第 i i i 到 j j j 的单词放在同一行的最小花费。使用如下递推公式来更新 c [ j ] c[j] c [ j ] :
c [ j ] = min 1 ≤ i ≤ j { c [ i − 1 ] + l c [ i − 1 ] [ j − 1 ] } c[j] = \min_{1 \leq i \leq j} \{c[i - 1] + lc[i-1][j-1]\}
c [ j ] = 1 ≤ i ≤ j min { c [ i − 1 ] + l c [ i − 1 ] [ j − 1 ]}
其中,c [ i − 1 ] c[i-1] c [ i − 1 ] 表示前 i − 1 i-1 i − 1 个单词的最小花费,l c [ i − 1 ] [ j − 1 ] lc[i-1][j-1] l c [ i − 1 ] [ j − 1 ] 表示将单词 i i i 到 j j j 放在同一行的立方空格花费。
返回最终结果:
c [ N ] c[N] c [ N ] 表示前 N N N 个单词的最小花费,即为所求的最小排版花费。
根据上述过程,建立extras和lc矩阵的时间复杂度为 O ( n 2 ) O(n^2) O ( n 2 ) ,空间复杂度为 O ( n 2 ) O(n^2) O ( n 2 ) ;求解 c [ N ] c[N] c [ N ] 的时间复杂度为 O ( n M ) O(nM) O ( n M ) ,其中 M M M 是一行可以容纳的字符数,空间复杂度为 O ( n ) O(n) O ( n ) 。
代码
定义类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> #include <vector> #include <climits> using namespace std;class Solution {public : int prettyPrint (vector<int >& length, int M) { int N = length.size (); vector<vector<int >> extras (N, vector <int >(N, 0 )); vector<vector<int >> lc (N, vector <int >(N, 0 )); vector<int > c (N + 1 , INT_MAX) ; for (int i = 0 ; i < N; i++) { extras[i][i] = M - length[i]; for (int j = i + 1 ; j < N; j++) { extras[i][j] = extras[i][j - 1 ] - length[j] - 1 ; } } for (int i = 0 ; i < N; i++) { for (int j = i; j < N; j++) { if (extras[i][j] < 0 ) { lc[i][j] = INT_MAX; } else if (j == N - 1 ) { lc[i][j] = 0 ; } else { lc[i][j] = extras[i][j] * extras[i][j] * extras[i][j]; } } } c[0 ] = 0 ; for (int j = 1 ; j <= N; j++) { for (int i = 1 ; i <= j; i++) { if (c[i - 1 ] != INT_MAX && lc[i - 1 ][j - 1 ] != INT_MAX) { c[j] = min (c[j], c[i - 1 ] + lc[i - 1 ][j - 1 ]); } } } return c[N]; } };
主函数
1 2 3 4 5 6 7 int main () { Solution sol; vector<int > lengths = {3 , 2 , 2 , 5 }; int M = 6 ; cout << "Minimum cost: " << sol.prettyPrint (lengths, M) << endl; return 0 ; }
运行结果