动态规划(Dynamic Programming, DP)

在查找有很多重叠子问题的情况的最优解时有效。它将问题重新组合成子问题。为了避免多次解决这些子问题,它们的结果都逐渐被计算并被保存,从简单的问题直到整个问题都被解决。因此,动态规划
保存递归时的结果,因而不会在解决同样的问题时花费时间 · · · · · · 动态规划只能应用于有最优 子结构的问题。最优子结构的意思是局部最优解能决定全局最优解(对有些问题这个要求并不能
完全满足,故有时需要引入一定的近似)。简单地说,问题能够分解成子问题来解决。

「状态定义」
「状态转移方程」
「初始化」
「输出」
「是否可以空间优化」

5. 最长回文子串

//5. 最长回文子串
    string longestPalindrome(string s) {
        /* 思路说明
        dp[i][j] 表示:子串 s[i..j] 是否为回文子串(子串 s[i..j]
        初始状态:所有长度为 1 的子串都是回文串
        从长度为len=2开始到len=n-1依次枚举所有长度的子串(注:如果len=n-1时某个字串是回文串,当len=n时只需判断此时子串的头尾位置元素是否相等即可判断)
        */


        int n = s.size();
        if (n < 2) 
        {
            return s;
        }

        int maxLen = 1;
        int begin = 0;
        // dp[i][j] 表示 s[i..j] 是否是回文串
        vector<vector<int>> dp(n, vector<int>(n));
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < n; i++) 
        {
            dp[i][i] = true;
        }
        // 递推开始
        // 先枚举子串长度
        for (int L = 2; L <= n; L++) 
        {
            // 枚举左边界,左边界的上限设置可以宽松一些
            for (int i = 0; i < n; i++) 
            {
                // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                int j = L + i - 1;
                // 如果右边界越界,就可以退出当前循环
                if (j >= n) 
                {
                    break;
                }

                if (s[i] != s[j]) 
                {
                    dp[i][j] = false;
                }
                else 
                {
                    if (j - i < 3) //长度为2,头尾元素相等 即为回文串
                    {
                        dp[i][j] = true;
                    }
                    else 
                    {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                // 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
                if (dp[i][j] && j - i + 1 > maxLen) 
                {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substr(begin, maxLen);
    }



70. 爬楼梯

//70. 爬楼梯
    int climbStairs(int n) {

        /*
            每次可以走一步或者两步,因此第 i 阶可以从第 i-1 或 i-2 阶到达。
            即到第 i 阶的方法数即为走到第 i-1 阶的方法数加上走到第 i-2 阶的方法数。
            状态转移方程:dp[i] = dp[i-1] + dp[i-2]
        */
        if (n < 3)
        {
            return n;
        }
        vector<int> dp(n + 1, 1);
        for (int i = 2; i <= n; ++i) 
        {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }



53. 最大子数组和

//53. 最大子数组和
    int maxSubArray(vector<int>& nums) {
        
        int n = nums.size();
        
        // dp[i] 表示:以 nums[i] 结尾的连续子数组的最大和,初始状态为dp[0]=nums[0]
        vector<int> dp(n);

        //初始状态即以nums[0]结尾的连续子数组的最大和即nums[0]
        dp[0] = nums[0];

        //从nums[1]开始遍历依次获取 i=1...n-1的所有以nums[i]结尾的连续子数组的最大和存入dp
        for (int i = 1; i < n; i++) {

            /*
            当dp[i - 1]>0 , dp[i] = dp[i - 1] + nums[i]
            当dp[i - 1]<=0, dp[i] = nums[i]
            */
            if (dp[i - 1] > 0) 
            {
                dp[i] = dp[i - 1] + nums[i];
            }
            else 
            {
                dp[i] = nums[i];
            }
        }

        // 从dp中获取连续子数组的最大和
        int res = dp[0];
        for (int i = 1; i < n; i++) {
            res = max(res, dp[i]);
        }
        return res;
    }



45. 跳跃游戏 II

//45. 跳跃游戏 II
    int jump(vector<int>& nums) {

        /*
            定义steps[i]为到达第i个位置需要的步数;
            当到达第i个位置时,此时可以跳跃的步数为nums[i];
            所以下标范围为[i+1,i+nums[i]]的这些位置,仅仅需要一步即可到达.即steps[i+1] ..... steps[i+nums[i]] = steps[i] + 1;

        */
        int len = nums.size();

        // dp[i] 表示:以 到达位置i的最小步数
        vector<int> dp(len, INT_MAX);
        dp[0] = 0;

        //当到达第i个位置时
        for (int i = 0; i < len; i++) 
        {     
            //可以向后跳跃[1,nums[i]]步
            for (int j = 1; j <= nums[i]; j++) 
            {    
                if (i + j < len)
                {
                    dp[i + j] = min(dp[i + j], dp[i] + 1);
                    //所以steps[i+j]只需要在steps[i+j]和steps[i]+1中取最小值即可
                }    
            }
        }
        return dp[len - 1];
    }

Simple
10 声望4 粉丝