[关闭]
@LIUHUAN 2019-12-29T17:41:34.000000Z 字数 14570 阅读 1641

动态规划

algorithm


最长连续递增子序列

1、思路一

  1. int findLengthOfLCIS(vector<int>& nums) {
  2. int n = nums.size();
  3. if(n <= 1)
  4. return n;
  5. int maxlen = 1;
  6. for(int i = 0; i < n;i++ ) {
  7. int len = 1;// 每次nums[i] 开始的序列最大长度为1,往后增长
  8. for(int j = i+ 1;j < n;j++){
  9. if(nums[j] > nums[j-1] ) {// 连续递增
  10. len += 1; // 长度增加
  11. maxlen = max(maxlen,len);
  12. }else {// 不连续递增了,下一个元素继续计算
  13. break;
  14. }
  15. }
  16. return maxlen;
  17. }

2、思路2

  1. int findLengthOfLCIS(vector<int>& nums) {
  2. int n = nums.size();
  3. if(n <= 1)
  4. return n;
  5. vector<int> dp(n,1);
  6. int maxlen = 1;
  7. for(int i = 1; i < n; i++ ) {
  8. if( nums[i] > nums[i-1] ) {
  9. dp[i] = dp[i-1] + 1 ;
  10. }
  11. maxlen = max( dp[i] , maxlen );
  12. }
  13. return maxlen;
  14. }

最长递增子序列

1、思路1

  1. int lengthOfLIS(vector<int>& nums) {
  2. int n = nums.size();
  3. if(n <= 1 )
  4. return n;
  5. vector<int> dp(n,1);// 初始化为1
  6. int max_len = 1;
  7. for(int i = 1 ;i < n; i++) {
  8. for(int j = i-1 ;j >=0 ;j-- ) { // 遍历[0,i-1]的元素加上nums[i] 之后的长度是否构成更长的递增子序列
  9. if(nums[j] < nums[i]) {
  10. dp[i] = max(dp[i],dp[j] + 1 );
  11. max_len = max(max_len,dp[i]);
  12. }
  13. }
  14. }
  15. return max_len;
  16. }

最长递增子序列的数量

1、思路1

  1. int findNumberOfLIS(vector<int>& nums) {
  2. int n = nums.size();
  3. if(n <= 1)
  4. return n;
  5. vector<int> dp(n,1); // nums[i]结尾的最长递增子序列长度
  6. int max_len = 1;//最大长度
  7. vector<int> max_c(n,1);// 每个最长子序列的长度默认为1
  8. for(int i = 1; i < n; i++ ){
  9. for(int j = i - 1; j >= 0; j-- ) {
  10. if(nums[i] > nums[j]) {
  11. if(dp[i] < dp[j] + 1){// dp[i] 可以从dp[j] 扩展得到更长的子序列,那么最长子序列的个数就是扩展前dp[j]的个数,max_c[j]
  12. max_c[i] = max_c[j]; // 扩展后的来源个数
  13. }else if(dp[i] == dp[j] + 1) { // 相同长度的其他扩展而来的子序列
  14. max_c[i] += max_c[j] ; // 因为长度相同,所以求和来源的个数
  15. }
  16. dp[i] = max(dp[i],dp[j] + 1);
  17. max_len = max(dp[i],max_len);
  18. }
  19. }
  20. }
  21. // 遍历所有的dp[i]等于最大长度时,求和所有的max_c[i],得到最大长度递增子序列的个数和
  22. int maxc = 0;
  23. for(int i = 0; i < n ; i++) {
  24. if(dp[i] == max_len) {
  25. maxc += max_c[i];
  26. }
  27. }
  28. return maxc;
  29. }

最长配对链

思路1

  1. int findLongestChain(vector<vector<int>>& pairs) {
  2. int n = pairs.size();
  3. if(n <= 1)
  4. return n;
  5. sort(pairs.begin(),pairs.end(),[](vector<int>& a,vector<int>&b ){return a[0] < b[0];});
  6. vector<int> dp(n,1);
  7. int max_len = 1;
  8. for(int i =1;i < n; i++) {
  9. int ifirst = pairs[i][0];
  10. for(int j =i -1 ;j >= 0; j--) {
  11. if(pairs[j][1] < ifirst) { // 构成递增链
  12. dp[i] = max(dp[i],dp[j] + 1); //更新递增链大小
  13. max_len = max(max_len,dp[i]);
  14. }
  15. }
  16. }
  17. return max_len;
  18. }

子数组等差数列的个数

思路1

  1. int numberOfArithmeticSlices(vector<int>& A) {
  2. int n = A.size();
  3. if(n <= 2 )
  4. return 0;
  5. vector<vector<int> > dp(n,vector<int>(n,0));
  6. for(int i = 1; i < n-1; i++) {
  7. dp[i-1][i+1] = (A[i] -A[i-1] == A[i+1] -A[i] );
  8. }
  9. for(int k = 3; k < n ;k++ ) {
  10. for(int i = 0; i < n; i++ ) {
  11. int j = i + k;
  12. if(j >= n)
  13. break;
  14. dp[i][j] = dp[i][j-1] && (A[j] - A[j-1] == A[j-1] - A[j-2]);
  15. }
  16. }
  17. int c = 0;
  18. for(int i =0 ;i < n; i++) {
  19. for(int j = i+2;j < n; j++) {
  20. if(dp[i][j] == 1)
  21. c += 1;
  22. }
  23. }
  24. return c;
  25. }

子序列的最长等差数列长度

思路1

  1. int longestArithSeqLength(vector<int>& A) {
  2. int n = A.size();
  3. vector<vector<int>> dp(n,vector<int>(n,2));
  4. for(int i = 0; i < n; i++ ) {
  5. dp[i][i] = 0;
  6. }
  7. int maxlen = 2;
  8. for(int i = 1; i < n; i++ ) {
  9. for(int j = i + 1; j <n ; j++) {
  10. int ak = 2*A[i] - A[j]; // 查找A[j] - A[i] == A[i] - A[k]
  11. for(int k = 0; k < i ;k++) {
  12. if(A[k] == ak) {
  13. dp[i][j] = max(dp[i][j] , dp[k][i] + 1 );
  14. maxlen = max(dp[i][j],maxlen);
  15. }
  16. }
  17. }
  18. }
  19. return maxlen;
  20. }

等差子序列的个数

思路1

  1. int numberOfArithmeticSlices(vector<int>& A) {
  2. int n = A.size();
  3. vector<vector<int>> dp(n,vector<int>(n,0));
  4. int sum = 0;
  5. for(int i = 0; i < n; i++ ) {
  6. for(int j = i + 1; j < n; j++ ) {
  7. // int ak = 2*A[i] - A[j]; // mind overflow !!!
  8. long t = A[j];
  9. t -= A[i];
  10. for(int k = 0; k < i; k++ ) {
  11. long x = A[i];
  12. x -= A[k];
  13. if(t == x ) {
  14. dp[i][j] += dp[k][i] + 1;
  15. }
  16. }
  17. sum += dp[i][j];
  18. }
  19. }
  20. return sum;
  21. }

二维问题

最小的赢得游戏代价

题目理解及思路

  1. int getMoneyAmount(int n) {
  2. vector<vector<int>> dp(n+1,vector<int>(n+1,INT_MAX));// 默认最大代价
  3. for(int i = 1;i < n ; i++) {
  4. dp[i][i+1] = i; // 初始情况步长为1
  5. dp[i][i] = 0;
  6. }
  7. dp[0][0] = 0;
  8. dp[n][n] = 0;
  9. int k = 2;// 步长为2
  10. for(int k =2 ;k <= n; k++ ) {
  11. for(int i = 1;i <= n; i++) {
  12. int j = i + k;// [i,j]范围内
  13. if(j > n)
  14. break;
  15. int mincost = INT_MAX;
  16. for(int m = i+1; m < j;m++) { // [i,m-1],[m+1,j]
  17. int t = max(dp[i][m-1],dp[m+1][j]) + m; // 选择猜测m,之后的最保险策略(能够猜到候选数的最大代价,付出这个代价,无论第二步,第三步,怎么猜测,一定能够找到候选数)
  18. mincost = min(mincost,t); // 选择一个最小,最为[i,j]的猜测代价
  19. }
  20. dp[i][j] = mincost;
  21. }
  22. }
  23. return dp[1][n]; // 返回[1,n]最小代价
  24. }

预测胜利者

  1. bool PredictTheWinner(vector<int>& nums) {
  2. int n = nums.size();
  3. vector<vector<int>> dp(n,vector<int>(n,0));
  4. int sum = 0;
  5. for( int i = 0; i < n; i++ ) {
  6. dp[i][i] = nums[i];
  7. sum += nums[i];
  8. }
  9. for( int k = 1; k < n; k++ ) {
  10. for( int i = 0; i < n; i++ ) {
  11. int j = i + k ;
  12. if(j >= n )
  13. break;
  14. int ni = min(getij (dp,i+1,j-1),getij(dp,i+2,j)) + nums[i];
  15. int nj = min(getij(dp,i+1,j-1),getij(dp,i,j-2)) + nums[j];
  16. dp[i][j] = max( ni ,nj );
  17. }
  18. }
  19. return dp[0][n-1] >= sum - dp[0][n-1];
  20. }
  21. int getij(vector<vector<int>>& dp,int i,int j) {
  22. int n = dp.size();
  23. if( i >= n || j >= n || i < 0 || j < 0 || i > j )
  24. return 0;
  25. return dp[i][j];
  26. }

石头游戏

思路1

  1. bool stoneGame(vector<int>& piles) {
  2. int m = piles.size();
  3. // int n = piles[0].size();
  4. vector<vector<int>> dp(m,vector<int>(m,0));
  5. int sum = 0;
  6. for(int i = 0 ;i < m ;i++) {
  7. sum += piles[i];
  8. }
  9. for(int k = 1; k < m ;k+=2) {
  10. for(int i = 0 ;i < m;i++) {
  11. int j = i + k ;
  12. if(j >= m)
  13. break;
  14. int front = piles[i] + max(getij(dp,i+1,j-1),getij(dp,i+2,j) );
  15. int end = piles[j] + max(getij(dp,i,j-2) , getij(dp,i+1,j-1) );
  16. dp[i][j] = max(front,end);
  17. }
  18. }
  19. return sum - dp[0][m-1] < dp[0][m-1];
  20. }
  21. int getij(vector<vector<int>>& dp,int i ,int j){
  22. int m = dp.size();
  23. if(i < 0 || j >= m || i >= m || j < 0)
  24. return 0;
  25. return dp[i][j];
  26. }

完美平方数个数

思路1

  1. int numSquares(int n) {
  2. if(n <= 0)
  3. return 0;
  4. if(n == 1)
  5. return 1;
  6. vector<int> dp(n+1,0);
  7. dp[1] = 1;
  8. for(int i = 2;i <= n; i++ ) {
  9. int m = int(sqrt(i)); // 不会超过这
  10. int t = INT_MAX;
  11. for(int k = 1 ;k <= m;k++){ // 从1开始遍历
  12. int j = k * k;
  13. t = min(dp[i-j] +1 ,t);//最小值
  14. }
  15. dp[i] = t;
  16. }
  17. return dp[n];
  18. }

最长斐波那切数列

思路0


- 初始状态:

思路1-暴力搜索

  1. // n^3
  2. int lenLongestFibSubseq(vector<int>& A) {
  3. int n = A.size();
  4. vector<vector<int>> dp(n,vector<int>(n,2));// 除自己与自己外,所有的序列长度默认为2
  5. int maxlen = 0;
  6. for(int i = 0; i < n; i++ ) {
  7. dp[i][i] = 0; // 自己与自己构成的序列长度设置为0
  8. for(int j = i + 1; j< n; j++ ) { // (A[i],A[j])
  9. for(int k = 0; k < i; k++ ) { // 对k执行搜索,0<= k < i
  10. if(A[i] + A[k] == A[j]) { // A[j] 与(A[k],A[i])可以构成更长的序列
  11. dp[i][j] = max(dp[i][j],dp[k][i] + 1);
  12. maxlen = max(maxlen,dp[i][j]);
  13. }
  14. }
  15. }
  16. }
  17. return maxlen;
  18. }

思路2

  1. int lenLongestFibSubseq(vector<int>& A) {
  2. int n = A.size();
  3. vector<vector<int>> dp(n,vector<int>(n,2));
  4. int t,maxlen = 0;
  5. for(int i = 0;i < n; i++) {
  6. dp[i][i] = 0;
  7. for(int j = i+1; j< n; j++) {
  8. int ak = A[j] - A[i];
  9. auto loc = lower_bound(A.begin(),A.begin() + i,ak); // 查找A[k]
  10. if(*loc == ak) {
  11. int k = loc - A.begin();
  12. dp[i][j] = max(dp[i][j],dp[k][i] + 1);
  13. maxlen = max(maxlen,dp[i][j]);
  14. }
  15. }
  16. }
  17. return maxlen;
  18. }

思路3

  1. int lenLongestFibSubseq(vector<int>& A) {
  2. int n = A.size();
  3. vector<vector<int>> dp(n,vector<int>(n,2));
  4. int t,maxlen = 0;
  5. unordered_map<int,int> umap;
  6. for(int i = 0; i < n; i++ ) {
  7. umap[A[i]] = i; // 存储所有的值与下标
  8. dp[i][i] = 0;
  9. }
  10. for(int i = 0;i < n; i++) {
  11. for(int j = i + 1; j< n; j++) {
  12. int ak = A[j] - A[i];
  13. auto e = umap.find(ak); // 查找A[k]
  14. if(e != umap.end() && e->second < i ) { // be careful the e->second < i
  15. int k = e->second;
  16. dp[i][j] = max(dp[i][j], dp[k][i] + 1);
  17. maxlen = max(maxlen, dp[i][j]);
  18. }
  19. }
  20. }
  21. return maxlen;
  22. }

最长摆动序列

思路1

  1. int wiggleMaxLength(vector<int>& nums) {
  2. int n = nums.size();
  3. if(n <= 0)
  4. return 0;
  5. vector<vector<int>> dp(n,vector<int>());
  6. for(int i = 0 ;i < n; i++ ) {
  7. dp[i] = {1,1};
  8. }
  9. int maxlen = 1;
  10. for( int i = 1; i < n; i++ ) {
  11. for( int j = i - 1; j >=0; j-- ) {
  12. if(nums[j] > nums[i]) { // 构成上摆数组
  13. dp[i][1] = max(dp[i][1] ,dp[j][0] + 1);
  14. }else if( nums[j] < nums[i] ) { // 构成下摆数组
  15. dp[i][0] = max(dp[i][0] ,dp[j][1] + 1);
  16. }
  17. maxlen = max(maxlen,dp[i][0]);
  18. maxlen = max(maxlen,dp[i][1]);
  19. }
  20. }
  21. return maxlen;
  22. }

最大乘积数组

思路1

思路2

  1. int maxProduct(vector<int>& nums) {
  2. int n = nums.size();
  3. vector<vector<int>> dp(n,vector<int>());
  4. if( n <=0 )
  5. return 0;
  6. for(int i = 0; i < n; i++ ) {
  7. dp[i] = {nums[i],nums[i]};
  8. }
  9. int maxv = nums[0];
  10. for(int i = 1; i < n; i++ ) {
  11. int a = nums[i];
  12. if( a < 0) {
  13. dp[i][0] = max(dp[i-1][1] * a ,a);
  14. dp[i][1] = min(dp[i-1][0] * a, a);
  15. }else if( a > 0) {
  16. dp[i][0] = max(dp[i-1][0] * a, a);
  17. dp[i][1] = min(dp[i-1][1] * a, a);
  18. }
  19. maxv = max(maxv,dp[i][0]);
  20. }
  21. return maxv;
  22. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注