leetcode-动态规划-汇总

1025.除数博弈

两个思路:

  • 常规dp
class Solution {
public:
    bool divisorGame(int N) {
        bool dp[1000+1];
        dp[1]=false;
        dp[2]=true;
        if(N <3){
            return dp[N];
        }
        else{
            for(int i=3;i<=N;i++){
                int cnt=0;
                for(int j=1;j<=1 ;j++){
                    if(dp[i-j]==false){//爱丽丝先取出j,搜寻以前的情况为i-j时爱丽丝的输赢,这就是接下来的鲍勃的输赢。(只要有一个能让鲍勃输,就有赢的可能,因为爱丽丝状态很好,有方法就一定会让自己赢)
                        cnt++;
                        break}
                }
                if(cnt!=0){
                    dp[i]=true;
                }else{
                    dp[i]=false;
                }
            }
            return dp[N];
        }
    }
};
  • 博弈问题 -> 数学问题(判断奇偶就够了)
class Solution {
public:
    bool divisorGame(int N) {
        return N%2 == 0;
};
303.区域和检索-数组不可变
  • 求得前缀和数组 sum[ n ],每次求区间[ i, j ],返回 sum[ j ] - sum[ i - 1];
121.买卖股票的最佳时期
53.最大子序和

题目要求至少选一个

  • 原来这也叫动态规划(因为真正的暴力解法没有备忘录?
int maxSubArray(int* nums, int numsSize){
    int max_sum = 0;
    int sum = 0;
    int num_max = nums[0];
    for(int i = 0; i < numsSize; i++){
        sum = sum + nums[i];
        if(sum < 0) 
        	sum = 0;
        if(sum > max_sum) 
        	max_sum = sum;
        if(nums[i]>num_max) 
        	num_max = nums[i];
    }
    if(max_sum == 0)//表明没有正的序列和,也就意味着都是负数,由于必须要选一个,那么就输出最大的负数
        max_sum = num_max;
    return max_sum;
}
化简:
int maxSubArray(int* nums, int numsSize){
    int max_sum = nums[0];
    int sum = 0;
    for(int i = 0; i < numsSize; i++){
        if(sum > 0){//sum当作一个数,sum > 0,保留这个数
            sum = sum + nums[i];
        }else{//sum < 0,没有增益作用,舍弃这个数
            sum = nums[i];
        }
        //sum = max(nums[i], sum + nums[i])
        //(sum > 0)相当于((sum + num[i]) > num[i]) 和上面的if判断一样
        //以每个位置为终点的最大子数列 都是基于其前一位置的最大子数列计算得出,
        //这样看比较dp    
        if(sum > max_sum){//不断更新max_sum
            max_sum = sum;
        }
    }
    return max_sum;
}
  • 分治算法,其实就是它的最大子序和要么在左半边,要么在右半边,要么是穿过中间,对于左右边的序列,情况也是一样,因此可以用递归处理。中间部分的则可以直接计算出来,时间复杂度应该是 O(nlogn)。代码如下:
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        #递归终止条件
        if n == 1:
            return nums[0]
        else:
            #递归计算左半边最大子序和
            max_left = self.maxSubArray(nums[0:len(nums) // 2])
            #递归计算右半边最大子序和
            max_right = self.maxSubArray(nums[len(nums) // 2:len(nums)])
        
        #计算中间的最大子序和,从右到左计算左边的最大子序和,从左到右计算右边的最大子序和,再相加
        max_l = nums[len(nums) // 2 - 1]
        tmp = 0
        for i in range(len(nums) // 2 - 1, -1, -1):
            tmp += nums[i]
            max_l = max(tmp, max_l)
        max_r = nums[len(nums) // 2]
        tmp = 0
        for i in range(len(nums) // 2, len(nums)):
            tmp += nums[i]
            max_r = max(tmp, max_r)
        #返回三个中的最大值
        return max(max_right,max_left,max_l+max_r)

看不懂。。

//分治法,时间复杂度o(n),优点是可以并行运算。
class Solution {
    public int maxSubArray(int[] nums) {
        return mergeCount(nums,0,nums.length)[2];
    }
    /**
     * @return 片段处理后的数组,依次为:左通最大值,右通最大值,局部最大值,总和
     * */
    public int[] mergeCount(int[] nums,int fromIndex,int toIndex){
        int[] result=new int[4];
        if(toIndex-fromIndex!=1){
            int midIndex=(toIndex+fromIndex)>>>1;
            int[] resL=mergeCount(nums,fromIndex,midIndex);
            int[] resR=mergeCount(nums,midIndex,toIndex);
            result[0]=Math.max(resL[0],resL[3]+resR[0]);
            result[1]=Math.max(resR[1],resL[1]+resR[3]);
            result[2]=Math.max(Math.max(resL[2],resR[2]),resL[1]+resR[0]);
            result[3]=resL[3]+resR[3];
            return result;
        }
        Arrays.fill(result,nums[fromIndex]);
        return result;
    }
}
392.判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
假设 s i s_{i} si t j t_{j} tj的子序列( j > = i j>=i j>=i):
s i s_{i} si != t j t_{j} tj ,当且仅当 s i s_{i} si t j − 1 t_{j-1} tj1 的子序列;
s i s_{i} si == t j t_{j} tj,当且仅当 s i − 1 s_{i-1} si1 t j − 1 t_{j-1} tj1 的子序列;

  • 按顺序遍历,相同字符就 cnt++ ,最后判断 cnt == s.size().
class Solution {
public:
    bool isSubsequence(string s, string t) {
        int j = 0 ;
        bool judge;
        for(int i = 0; i < t.size(); i++)
            if(t[i] == s[j])
                j++;
        if(j == s.size())
            judge = true;
        else
            judge = false;
        return judge;
    }
};
  • 用 dp 反而慢一点:
class Solution {
    public boolean isSubsequence(String s, String t) {
        int sLen = s.length(), tLen = t.length();
        if (sLen > tLen) return false;
        if (sLen == 0) return true;
        boolean[][] dp = new boolean[sLen + 1][tLen + 1];
        //初始化
        for (int j = 0; j < tLen; j++) {
            dp[0][j] = true;
        }
        //dp
        for (int i = 1; i <= sLen; i++) {
            for (int j = 1; j <= tLen; j++) {
                if (s.charAt(i - 1) == t.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = dp[i][j - 1];
                }
            }
        }
        return dp[sLen][tLen];
    }
}
  • 后续挑战 :
    如果有大量输入的 S,称作S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
    Answer: 用二维数组list[26][]记下A~Z出现的所有位置,每次查询 S i S_{i} Si,就依次查询(注意次序大小关系)
bool isSubsequence(string s, string t) {        
    vector<vector<int>>dp(26);
    int tag=-1;
    for(int i=0;i<t.size();i++)
        dp[t[i]-'a'].push_back(i);
    for(int i=0;i<s.size();i++){
        int now=s[i]-'a';
        int left=0,right=dp[now].size()-1;
        while(left<right){
            int mid=(left+right)/2;
            if(dp[now][mid]>tag)
                right=mid;
            else
                left=mid+1;
        }
        if(right<left || dp[now][left]<tag)return false;
        tag=dp[now][left];
    
    }
    return true;
}
70.爬楼梯
  • dp[ i ] = dp[ i - 1 ] + dp[ i - 2 ]
class Solution {
public:
    int climbStairs(int n) {
        int ans = 0;
        int dp[1000001];
        dp[1] = 1;
        dp[2] = 2;
        if(n < 3){
            ans = dp[n];
        }else{
            for(int i = 3;i<=n;i++){
                dp[i]=dp[i-1]+dp[i-2];
            }
            ans = dp[n];
        }
        return dp[n];
    }
};
优化,本质为斐波那契数列,空间复杂度为 O(1)class Solution {
public:
    int climbStairs(int n) {
        int third = 0;
        int dp[1000001];
        int first = 1;
        int second = 2;
        if(n = 1){
            ans = first;
        }else if(n == 2){
        	ans = second;
        }else{
            for(int i = 3;i<=n;i++){
               third = first + second;
               first = second;
               second = third;
            }
            ans = third;
        }
        return ans;
    }
};
746.使用最小花费爬楼梯
  • dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int dp[1001];
        dp[0] = 0;
        dp[1] = 0;
        for(int i = 2; i <= cost.size(); i++){
            dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]);
        }
        return dp[cost.size()];
    }
};
198.打家劫舍
  • dp[i] = max( dp[i-1] , dp[i-2] + nums[i] )
    dp[i] = max(不偷(也就无所谓昨天有没有偷,无脑继承),偷(继承前天的收益))
  • 另外,注意一下特殊情况的特判,否则会有溢出的内存错误
class Solution {
public:
    int rob(vector<int>& nums) {
        int dp[100001];
        if(nums.size() == 0){
            return 0;
        }else if(nums.size() == 1){
            return nums[0];
        }else{
            dp[0] = nums[0];
        	dp[1] = max(dp[0] , nums[1]);
        	for(int i = 2;i < (int)nums.size(); i++){
            	dp[i] = max(dp[i-1], dp[i-2] + nums[i]);
        	}
        	return dp[(int)nums.size()-1];
        }
    }
};
338.比特位计数
  • 大概。。找规律。。
class Solution {
public:
    vector<int> countBits(int num) {
        vector<int> dp(num + 1);
        dp[0] = 0;
        int e = 1;
        for(int i = 1; i <= num; i++){
            if(e*2 == i){
                e = e*2;
            }
            dp[i] = 1 + dp[i - e];
        }
        return dp;
    }
};
//dp[num] = 1 + dp[num - 2^exp];
  • 奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1。
  • 举例: 
       0 = 0       1 = 1
       2 = 10      3 = 11
    
  • 偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。
  • 举例:
        2 = 10       4 = 100       8 = 1000
        3 = 11       6 = 110       12 = 1100
    
  • 另外,0 的 1 个数为 0,于是就可以根据奇偶性开始遍历计算了。
vector<int> countBits(int num) {
       vector<int> result(num+1);
       result[0] = 0;
       for(int i = 1; i <= num; i++){
           if(i % 2 == 1){
               result[i] = result[i-1] + 1;
           }
           else{
               result[i] = result[i/2];
           }
       }
       return result;
}
877.石子游戏
  • 方法一:动态规划

  • 思路:
    让我们改变游戏规则,使得每当李得分时,都会从亚历克斯的分数中扣除。
    令 dp(i, j) 为亚历克斯可以获得的最大分数,其中剩下的堆中的石子数是 piles[i], piles[i+1], …, piles[j]。
    这在比分游戏中很自然:我们想知道游戏中每个位置的值。

  • 我们可以根据 dp(i + 1,j) 和 dp(i,j-1) 来制定 dp(i,j) 的递归,我们可以使用动态编程以不重复这个递归中的工作。该方法可以输出正确的答案,因为状态形成一个DAG(有向无环图)。

  • 算法:
    当剩下的堆的石子数是 piles[i], piles[i+1], …, piles[j] 时,轮到的玩家最多有 2 种行为
    可以通过比较 j-i和 N modulo 2 来找出轮到的人

  •   如果玩家是亚历克斯,那么她将取走 piles[i] 或 piles[j] 颗石子,增加她的分数。
      之后,总分为 piles[i] + dp(i+1, j) 或 piles[j] + dp(i, j-1);我们想要其中的最大可能得分。
    
  •   如果玩家是李,那么他将取走 piles[i] 或 piles[j] 颗石子,减少亚历克斯这一数量的分数。
      之后,总分为 -piles[i] + dp(i+1, j) 或 -piles[j] + dp(i, j-1);我们想要其中的最小可能得分。
    
class Solution {
public:
    bool stoneGame(vector<int>& piles) {
        int N = piles.size();

        // dp[i+1][j+1] = the value of the game [piles[i], ..., piles[j]]
        int dp[N+2][N+2];
        memset(dp, 0, sizeof(dp));

        for (int size = 1; size <= N; ++size)
            for (int i = 0, j = size - 1; j < N; ++i, ++j) {
                int parity = (j + i + N) % 2;  // j - i - N; but +x = -x (mod 2)
                if (parity == 1)
                    dp[i+1][j+1] = max(piles[i] + dp[i+2][j+1], piles[j] + dp[i+1][j]);
                else
                    dp[i+1][j+1] = min(-piles[i] + dp[i+2][j+1], -piles[j] + dp[i+1][j]);
            }

        return dp[1][N] > 0;
    }
};
  • 复杂度分析
    时间复杂度: O ( N 2 ) O(N^{2}) O(N2),其中 N N N 是石子堆的数目。
    空间复杂度: O ( N 2 ) O(N^{2}) O(N2),该空间用以存储每个子游戏的中间结果。

  • 方法二:数学

    思路和算法
    显然,亚历克斯总是赢得 2 堆时的游戏。 通过一些努力,我们可以获知她总是赢得 4 堆时的游戏。
    如果亚历克斯最初获得第一堆,她总是可以拿第三堆。 如果她最初取到第四堆,
    她总是可以取第二堆。第一 + 第三,第二 + 第四 中的至少一组是更大的,
    所以她总能获胜。
    我们可以将这个想法扩展到 N 堆的情况下。设第一、第三、第五、第七桩是白色的,
    第二、第四、第六、第八桩是黑色的。 
    亚历克斯总是可以拿到所有白色桩或所有黑色桩,
    其中一种颜色具有的石头数量必定大于另一种颜色的。
    因此,亚历克斯总能赢得比赛。
    
class Solution {
public:
    bool stoneGame(vector<int>& piles) {
        return true;
    }
};
  • 复杂度分析
    时间和空间复杂度: O ( 1 ) O(1) O(1)
拓展后的石子游戏

leetcode-解决博弈问题的动态规划通用思路 sdl wsl

  • 石子总数为n(为任意正整数),分为m堆(任意正整数)。

    - -初始化vector二维数组
    //初始化行数为maxm,pair<int,int>型的二维数组
    vector<vector<pair<int,int>>> dp(maxm);
    for(int i=0;i<m;i++){
          dp[i].resize(m);//对每一行修改列值
      }
    

    斜着填表

#include <cstdio>
#include <vector>
#define maxm 1001
using namespace std;

int n,m;
vector<vector<pair<int,int>>> dp(maxm);//初始化行数为maxm,pair<int,int>型的二维数组
vector<int> list(maxm);

int main(){
    while (~scanf("%d %d",&n,&m)){
        for(int i=0;i<m;i++){
            dp[i].resize(m);//对每一行修改列值
        }
        for(int i=0;i<m;i++){
            scanf("%d",&list[i]);
        }
        for(int i=0;i<m;i++){
            dp[i][i].first=list[i];
            dp[i][i].second = 0;
            for(int j=i+1;j<m;j++){
                dp[i][j].first = 0;
                dp[i][j].second = 0;
            }
        }
        //观察dp方程,需要i+1和j-1,故是斜着遍历数组的
        for(int l=1;l<m;l++){
            for(int i=0;i<m;i++){
                int j=i+l;
                if(j>=m){
                    break;
                }
                int left = list[i]+dp[i+1][j].second;//先选左边,下一轮变成后手
                int right = list[j]+dp[i][j-1].second;
                if(left > right){
                    dp[i][j].first = left;
                    dp[i][j].second = dp[i+1][j].first;//下一轮的先手的最优解,两人都是精明的
                }else{
                    dp[i][j].first = right;
                    dp[i][j].second = dp[i][j-1].first;
                }
            }
        }
        int ans = dp[0][m-1].first-dp[0][m-1].second;
        if(ans>0){
            printf("FirstOneWins\n");
        }else if(ans<0){
            printf("SecondOneWins\n");
        }else{
            printf("NoOneWins\n");
    }
    return 0;
}
96.不同的二叉搜索树
  • 复习赛做过

    思路一:卡特兰数,然后递归
    
class Solution {
public:
    int numTrees(int n) {
        int list[40]={0,};
        list[0] = 1;
        list[1] = 1;
        list[2] = 2;
        list[3] = 5;
        list[4] = 14;
        list[5] = 42;
        int i,j,k;
        for(i = 6; i <= n;i++){
            for(j = 0, k = i - 1; j <= i - 1 && k >= 0; j++, k--){
                list[i] += list[j] * list[k];
            }
        }
        return list[n];
    }
};

class Solution {
  public int numTrees(int n) {
    // Note: we should use long here instead of int, otherwise overflow
    long C = 1;
    for (int i = 0; i < n; ++i) {
      C = C * 2 * (2 * i + 1) / (i + 2);
    }
    return (int) C;
  }
}
  • code2 用递归式:
    C 0 = 1 C_{0} = 1 C0=1
    C n + 1 = 2 ( 2 n + 1 ) / ( n + 2 ) ∗ C n C_{n+1} = 2(2n+1)/(n + 2) *C_{n} Cn+1=2(2n+1)/(n+2)Cn
  • 令h(1)=1,h(0)=1,catalan数(卡特兰数)满足递归式:
    h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (其中n>=2)
    
  • 另类递归式:
      h(n)=((4*n-2)/(n+1))*h(n-1);
    
  • 该递推关系的解为:
      h(n)=C(2n,n)/(n+1) (n=1,2,3,...)
    
64.最小路径和
  • 常规dp
  • dp[i][j] = min(dp[ i - 1 ][ j ],dp[ i ][ j - 1 ]) + grid[i][j]
                         下移得来 ,    从右移得来                 ,前面的均为最优解
    
  • 二维,要注意有边界限制
class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int dp[1001][1001];
        int m = grid.size();
        int n = grid[0].size();
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(i == 0 && j == 0){
                    dp[i][j] = grid[i][j];
                }
                if(i != 0 && j == 0){
                    dp[i][j] = dp[i-1][j] + grid[i][j];
                }
                if(i == 0 && j != 0){
                    dp[i][j] = dp[i][j-1] + grid[i][j];
                }
                if(i != 0 && j != 0){
                    dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
                }
            }
        }
        return dp[m-1][n-1];
    }
};
//可化简,在原数组上修改值(每处的最优解)
120.三角形最小路径和
  • dp[i][j] = max(dp[i-1][j],dp[i-1][j-1])
    注意(两侧)边界问题
    
  • 可以在原数组上修改
int the_min(int a,int b){
    return a<b?a:b;
}
class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int n = triangle.size();
        int dp[1001][1001];
        int min = INT_MAX;
        for(int i = 0; i < n; i++){
            for(int j = 0; j <= i; j++){
                if(j == 0 && i == 0){
                    triangle[i][j] = 0 + triangle[0][0];
                }
                else if(j == 0 && i != 0){
                    triangle[i][j] = triangle[i-1][j] + triangle[i][j];
                }
                else if(i == j){
                    triangle[i][j] = triangle[i-1][j-1] + triangle[i][j];
                }
                else if(i != 0 && j != 0 && i != j){
                    triangle[i][j] = the_min(triangle[i-1][j],triangle[i-1][j-1]) + triangle[i][j];
                }
                if(i == n-1){
                    if(triangle[i][j] < min){
                        min = triangle[i][j];
                    }
                }
                
            }
        }
        
        return min;
    }
};
95.不同的二叉搜索树Ⅱ
  • 递归
class Solution {
public:
    vector<TreeNode*> helper(int start,int end){
        vector<TreeNode*> ret;
        if(start > end)
            ret.push_back(nullptr);
        
        for(int i=start;i<=end;i++){
            vector<TreeNode*> left = helper(start,i-1);
            vector<TreeNode*> right = helper(i+1,end);
            for(auto l : left){
                for(auto r : right){
                    TreeNode* root = new TreeNode(i);
                    root -> left = l;
                    root -> right = r;
                    ret.push_back(root);
                }
            }
        }
        return ret;
    }
    
    vector<TreeNode*> generateTrees(int n) {
        vector<TreeNode*> ret;
        if(n == 0)
            return ret;    
        ret = helper(1,n);
        return ret;
    }
};
712.两个字符串的最小ASCII删除和
题意是寻找一个共同子序列,将字符串s1和s2删除为该子序列时所删除的ASCII综合最小。
等价于求一个字符串s1和s2的ASCII码总和最大的共同子序列。
因为s1和s2的总和固定,当共同子序列的总和最大时,删除成为该子序列的代价必然最小。

注意几个特判(i == 0,j == 0)
class Solution {
public:
    int minimumDeleteSum(string s1, string s2) {
        int dp[1001][1001];
        int sum1 = 0, sum2 = 0;
        for(int i = 0; i < s1.size(); i++){
            sum1 += s1[i];
            for(int j = 0; j < s2.size(); j++){
                if(i == 0 ) sum2 += s2[j];
                if(i == 0 || j == 0){
                    if(s1[i] == s2[j]){
                        dp[i][j] = 0 + s1[i];
                    }
                    else{//注意这里的几个特判,不能盲目的等于0
                        if( i == 0 && j == 0){
                            dp[i][j] = 0;
                        }
                        else if(i == 0){
                            dp[i][j] = dp[i][j - 1];
                        }
                        else if(j == 0){
                            dp[i][j] = dp[i - 1][j];
                        }
                    }
                }else{
                    if(s1[i] == s2[j]){
                        dp[i][j] = dp[i - 1][j - 1] + s1[i];
                    }else{
                        dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                    }
                }
            }
        }
        return sum1 + sum2 - 2 * dp[s1.size() - 1][s2.size() - 1];
    }
};
  • 这样好像避免了特判,也更快
class Solution {
public:
    int minimumDeleteSum(string s1, string s2) {
        //if (s1.empty() || s2.empty()); return 0;
        int len1 = s1.size();
        int len2 = s2.size();
        int sum1 = 0;
        int sum2 = 0;
        int i, j;
        vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));
        cout << s1[0] << endl;
        
        for (i = 0;i < len1; i++) {
            sum1 += s1[i];
            cout << sum1 << endl;
        }
        cout << sum1 << endl;
        for (j = 0;j < len2; j++) {
            sum2 += s2[j];
        }
        for (i = 0;i < len1; i++) {
            for (j = 0; j < len2; j++) {
                if(s1[i] == s2[j]) {
                    dp[i + 1][j + 1] = dp[i][j] + s1[i];
                } else {
                    dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);
                }
            }
        }
        return sum1 + sum2 - 2 * dp[i][j];
    }
};
647.回文子串
执行代码和提交代码结果不同 wsl 跳过吧
哦。。没初始化dp
class Solution {
public:
    int countSubstrings(string s) {
        int len = s.size();
        bool dp[1001][1001];
        int cnt = 0;
        for(int i=0;i<len-1;i++){
            dp[i][i] = true;
            cnt++;
            if(s[i]==s[i+1]){
                dp[i][i+1] = true;
                cnt++;
            }
        }
        dp[len-1][len-1] = true;
        cnt++;
        for(int k = 3 ; k <= len ;k++){
            for(int i = 0 ;i< len - k + 1;i++){
                if(dp[i+1][i+k-2]&&s[i]==s[i+k-1]){
                    dp[i][i+k-1] = true;
                    cnt++;
                }
            }
        }
        return cnt;
    }
};
714.买卖股票的最佳时机含手续费
class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        int dp_i_0 = 0;
        int dp_i_1 = INT_MIN;
        for(int i =0 ;i <n;i++){
            int temp = dp_i_0;
            dp_i_0 = max(dp_i_0,dp_i_1+prices[i]);
            dp_i_1 = max(dp_i_1,temp-prices[i]-fee);
            
        }
        return dp_i_0;
    }
};
931.下降路径最小和
  • 常规dp

    dp[i][j] = min(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1]);
    若在最左侧,来自2,3
    若在最右侧,来自1,2
    
class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& A) {
        int n = A.size();
        int min_num=INT_MAX;
        for(int i = 0;i<n;i++){
            for(int j= 0;j<n;j++){
                if(i==0){
                	A[i][j] = A[i][j];
                }else if(j==0){
                    A[i][j] =min(A[i-1][j],A[i-1][j+1])+A[i][j];
                }else if(j==n-1){
                    A[i][j] =min(A[i-1][j-1],A[i-1][j])+A[i][j];
                }else{
                    A[i][j] =min(min(A[i-1][j-1],A[i-1][j]),A[i-1][j+1])+A[i][j];
                }
                if(i==n-1){
                    if(A[i][j]<min_num){
                        min_num = A[i][j];
                    }
                }
            }
        }
        return min_num;
    }
};
413.等差数列划分
  • 我是垃圾,是等差数列又不是回文,为什么要两边延申。。脑子瓦特了吧
class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        int n = A.size();
        int cnt =0;
        if(n==0){
            cnt = 0;
        }else{
            bool dp[8001][8001]={false,};
            for(int i=0;i<n-1;i++){
                dp[i][i] = true;
                dp[i][i+1] =true;
            }
            dp[n-1][n-1]=true;
            for(int i = 3; i <=n;i++){
                for(int j = 0;j<n-i+1;j++){
                    if(dp[j+1][j+i-2] == true && A[j]-A[j+1]==A[j+1]-A[j+2]&&A[j+i-1]-A[j+i-2]==A[j+i-2]-A[j+i-3]){
                        dp[j][j+i-1]  =true;
                        cnt++;
                    }
                }
            }
        }
        return cnt;
    }
};
//子数组至少三个数
  • 我们可以观察到区间 (0,i)(0,i) 中等差数列的个数只和这个区间中的元素有关。因此,这个问题可以用动态规划来解决。

  • 首先创建一个大小为 nn 的一维数组 dpdp。dp[i]dp[i] 用来存储在区间 (k,i) , 而不在区间 (k,j) 中等差数列的个数,其中 j<i,否则有重复,如123在123中,也在1234中。

  • 与递归方法中后向推导不同,我们前向推导 dpdp 中的值。其余的思路跟上一个方法几乎一样。对于第 ii 个元素,判断这个元素跟前一个元素的差值是否和等差数列中的差值相等。如果相等,那么新区间中等差数列的个数即为 1+dp[i-1]1+dp[i−1]。sumsum 同时也要加上这个值来更新全局的等差数列总数。

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        int n = A.size();
        int dp[10001] = {0,};
        int sum = 0;
        for(int i=2;i<n;i++){
            if(A[i]-A[i-1]==A[i-1]-A[i-2]){
                dp[i] = dp[i-1]+1;
                sum+=dp[i];
            }
        }
        return sum;
    }
};
//化简,只记录前一个量
class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        int n = A.size();
        int dp_i = 0;
        int sum = 0;
        for(int i=2;i<n;i++){
            if(A[i]-A[i-1]==A[i-1]-A[i-2]){
                dp_i = dp_i+1;
                sum+=dp_i;
            }else{
                dp_i = 0;
            }
        }
        return sum;
    }
};
62.不同路径
  • 常规dp
class Solution {
public:
    int uniquePaths(int m, int n) {
        int dp[101][101]={{0,0},};
        for(int i;i<m;i++){
            for(int j = 0;j <n;j++){
                if(i == 0&&j==0){
                    dp[i][j]  =1;
                }else if(i==0){
                    dp[i][j] = dp[i][j-1];
                }else if(j==0){
                    dp[i][j]  =dp[i-1][j];
                }else{
                    dp[i][j] = dp[i][j-1]+dp[i-1][j];
                }
            }
        }
        return dp[m-1][n-1];
    }
};
  • 一维做法,小学奥数?
class Solution {
    public int uniquePaths(int m, int n) {
        int[] memo = new int[n];
        Arrays.fill(memo, 1);
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                memo[j] += memo[j - 1];
            }
        }
        return memo[n - 1];
    }
}
1143.最长公共子序列
class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int dp[1001][1001];
        memset(dp, 0, sizeof(dp));
        int m = text1.size();
        int n = text2.size();
        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];
    }
};
683.大礼包
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值