zoukankan      html  css  js  c++  java
  • 动态规划题目汇总

    1、最短编辑距离:

    #include <iostream>
    #include <string.h>
    #include <vector>
    #include <algorithm>//min()包含头文件 sf
    using namespace std;
    int main(){
    	char str1[1025],str2[1025];//d//长度不超过1024,长度最小要声明为1024+1,因为字符串末尾有空字符。
    	int n,m,temp;
    	
    	cout<<"hhhhh llll"<<endl;
    
    	while(cin>>str1>>str2){//循环输入两个字符串
    		m=strlen(str1);
    		n=strlen(str2);
    
    		cout<<"长度1:"<<m<<endl;
    		cout<<"长度2:"<<n<<endl;
    
    
    		vector< vector<int> > dp(m+1,vector<int>(n+1,0));//生成一个m+1行n+1列的二维矩阵记录当前的状态值
    		//初始化
    		for(int i=1;i<=m;i++)//dp[i][0]=i,例如dp[2][0]表示一个长度为2的字符串str1与一个空字符串str2的最小编辑距离为2(即依次将str1中的字符添加到str2中)
    			dp[i][0]=i;
    		for(int j=0;j<=n;j++)//dp[0][j]=j,例如dp[0][1]表示一个空字符串str1与一个长度为1的字符串str2的最小编辑距离为1(即依次将str2中的字符添加到str1中)
    			dp[0][j]=j;
    		dp[0][0]=0;//空字符串与空字符串之间的最小编辑距离为0
    		for(int i=1;i<=m;i++){
    			for(int j=1;j<=n;j++){
    				if(str2[j-1]==str1[i-1])//注意:字符串str1和str2中的索引是从0开始的,而1<=i<=m,1<=j<=n,所以这里的i和j要减1
    					dp[i][j]=dp[i-1][j-1];
    				else{
    					temp=min(dp[i][j-1],dp[i-1][j]);
    					dp[i][j]=min(temp,dp[i-1][j-1])+1;
    				}	
    			}
    		}
    		cout<<dp[m][n]<<endl;//最终的dp[m][n]为两字符串之间的最小编辑距离
    	}
    	return 0;
    }
    

      

    2、背包问题:

    描述

    在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]

    你不可以将物品进行切割。

    样例

    样例 1:
    	输入:  [3,4,8,5], backpack size=10
    	输出:  9
    
    样例 2:
    	输入:  [2,3,5,7], backpack size=12
    	输出:  12
    	
    

     思路:

    dp[k][n] 的定义如下:对于前 k 个物品,当前背包的容量为 n ,这种情况下可以装的最大价值是dp[k][n]。

    解题框架:
    
    int dp[N+1][W+1]
    dp[0][..] = 0
    dp[..][0] = 0
    for i in [1..N]:
        for w in [1..W]:
            dp[i][w] = max( 把物品 i 装进背包,不把物品 i 装进背包 )
    
    return dp[N][W]
    

     

     
    class Solution {
    public:
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @return: The maximum size
         */
        int backPack(int m, vector<int> &A) {
            // write your code here
            int len = A.size()+1;
            m = m+1;
      
            vector<vector<int>> dp(len, vector<int> (m, 0));
            // int dp[len][m];
            // vector<vector<int>> dp[len][m];
            // for (int i=0;i<len;i++){
            //     dp[i][0] = 0;
            // }
            // for (int i= 0;i<m;i++){
            //     dp[0][i] = 0;
            // }
            
            for(int k =1;k<len;k++){
                for(int n=1;n<m;n++){
                    if(n-A[k-1]<0){
                        dp[k][n]=dp[k-1][n];
                    }
                    else{
                        dp[k][n]= max(dp[k-1][n],dp[k-1][n-A[k-1]]+A[k-1]);
                    }
                }
            }
            
            return dp[len-1][m-1];
        }
    };
    

      

    背包问题2

    描述

     n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V表示每个物品的价值.

    问最多能装入背包的总价值是多大?

    1. A[i], V[i], n, m 均为整数
    2. 你不能将物品进行切分
    3. 你所挑选的要装入背包的物品的总大小不能超过 m
    4. 每个物品只能取一次

    样例 1:

    输入: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
    输出: 9
    解释: 装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9 
    

    样例 2:

    输入: m = 10, A = [2, 3, 8], V = [2, 5, 8]
    输出: 10
    解释: 装入 A[0] 和 A[2] 可以得到最大价值, V[0] + V[2] = 10
    

    思路:和0-1背包问题的区别只在于当物品可以放入时,dp[k][n]= max(dp[k-1][n],dp[k-1][n-A[k-1]]+V[k-1]);
    这里时减去体积,加上价值;上面的问题价值和体积是一样的数值;
     
    class Solution {
    public:
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @param V: Given n items with value V[i]
         * @return: The maximum value
         */
        int backPackII(int m, vector<int> &A, vector<int> &V) {
            // write your code here
            int len = A.size()+1;
            m = m+1;
      
            vector<vector<int>> dp(len, vector<int> (m, 0));
            // int dp[len][m];
            // vector<vector<int>> dp[len][m];
            // for (int i=0;i<len;i++){
            //     dp[i][0] = 0;
            // }
            // for (int i= 0;i<m;i++){
            //     dp[0][i] = 0;
            // }
            
            for(int k =1;k<len;k++){
                for(int n=1;n<m;n++){
                    if(n-A[k-1]<0){
                        dp[k][n]=dp[k-1][n];
                    }
                    else{
                        dp[k][n]= max(dp[k-1][n],dp[k-1][n-A[k-1]]+V[k-1]);
                    }
                }
            }
            
            return dp[len-1][m-1];
        }
    };
    

      

    3、连续最大和

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:

    输入: [-2,1,-3,4,-1,2,1,-5,4]
    输出: 6
    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

    思路:动态数组dp[i]表示,以第i个元素最为结尾的子数组,连续最大和是多少;

    对每个元素i,都有两种选择,加入子数组或者单独作为子数组的开始元素;

    所以:dp[i] = max(nums[i],nums[i]+dp_1)

    然后遍历dp数组取最大值即可;

    或者采用一个遍历随时记录最大值,可以节省空间;

    -----------------------------------------

    事实上,当我们令curSum为当前最大子数组的和,maxSum为最后要返回的最大子数组的和

    1. (继承前人遗产吗) 当我们往后扫描时,对第j+1个元素有两种选择——要么放入前面找到的子数组,要么做为新子数组的第一个元素:
      1.1 如果currSum+当前元素a[j] >= a[j],则令currSum加上a[j]
      1.2 否则currSum重新赋值,置为下一个元素,即currSum = a[j]。
    2. (更新历代最强吗) 比较当前最大子数组和与最大子数组的和:
      2.1 同时,当currSum > maxSum,则更新maxSum = currSum
      2.2 否则保持原值,不更新。


    curSum = max(a[j], curSum + a[j])
    maxSum = max(maxSum, curSum)

    举个例子,当输入数组是1, -2, 3, 10, -4, 7, 2, -5,那么,curSum和maxSum相应的变化为:
    curSum : 0 | 1 -1 3 13 9 16 18 13
    maxSum : 1 | 1 1 3 13 13 16 18 18

    class Solution {
    public:
        int maxSubArray(vector<int>& nums) {
            int m = size(nums);
            if (m == 0) return 0;
            int dp_1= nums[0];
            int dp_2= 0;
            int res = dp_1;
            for (int i=1; i< m ; i++){
                dp_2 = max(nums[i],nums[i]+dp_1);
                dp_1 = dp_2;
                res = max(res,dp_2);
    
            }
            return res;
        }
    };
    

      

    class Solution {
    public:
        int maxSubArray(vector<int>& nums) {
            int m = size(nums);
            vector<int> dp(m);
            dp[0] = nums[0];
    
            for(int i=1;i<m;i++){
                dp[i]=max(nums[i],nums[i]+dp[i-1]);
            }
    
            int res=nums[0];
            for(auto n:dp){
                if(res<n){
                    res=n;
                }
            }
            return res;
        }
    };
    

      

    4、最长上升子序列

    给定一个无序的整数数组,找到其中最长上升子序列的长度。

    示例:

    输入: [10,9,2,5,3,7,101,18]
    输出: 4
    解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

    思路:与上题类似,dp[i]表示以nums[i]结尾的数组的最长递增子序列长度

    当前元素大于前面的元素时

    dp[i] = max(dp[j] +1 ) 此时,0<j<i 

    -----------------------------------------

    比如,处理最后一个元素 5,发现比前面的元素都大,那么以 5 结尾的 LIS 的长度可能为

    dp[0] + 1,dp[1] + 1,dp[2] + 1,dp[3] + 1。

    其中的最大值为 dp[2] + 1 = 3,因此 LIS 的长度为 3。

    总结:

    dp[i] 默认都为 1,因为以 i 结尾的 LIS 至少包含自己。

    在 dp 表 0~i-1 中比对时,若 arr[i] > arr[j],

    那么 dp[j] + 1 可能为 dp[i] 的最终值。

    需要在所有的可能值中取最大值。

    时间复杂度为 O(n2)

    https://blog.csdn.net/qq_41765114/article/details/88415541

    class Solution {
    public:
        int lengthOfLIS(vector<int>& nums) {
    
            int len = nums.size();
            vector<int > dp(len,0);
            if (len == 0){
                return 0;
            }
            if (len == 1){
                dp[0]= 1;
                return 1;
            }
    
            dp[0]=1;
            int res2 =1;
            for(int i=1;i<len;i++){
                int res =1;
                for(int j=0;j<=i;j++){
                    if(nums[i]>nums[j]){
                        res = max(res,dp[j]+1);
                    }
                }
                dp[i] = res;
                res2 = max(res,res2);
            }
            return res2;
        
        }
    };
    

      

    最长上升子序列:
    
    思路:dp[i]表示以nums[i]结尾的数组的最长递增子序列长度
    
    当前元素大于前面的元素时:
    dp[i] = max(dp[j] +1 ) 此时,0<j<i 
    
    class Solution {
    public:
        int lengthOfLIS(vector<int>& nums) {
            int len = nums.size();
            if(len==0){
                return 0;
            }
            if(len==1){
                return 1;
            }
    
            vector<int> dp(len);
            dp[0]=1;
    
            int res=1;
    
            for(int i=1;i<len;i++){
                int temp=1;
                for(int j=0;j<i;j++){
                    if(nums[i]>nums[j]){
                        temp=max(temp,dp[j]+1);
                    }
                }
                dp[i]=temp;
                res = max(res,dp[i]);
            }
            return res;
        }
    };
    

      

    5、乘积最大子数组

    给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

    示例 1:

    输入: [2,3,-2,4]
    输出: 6
    解释: 子数组 [2,3] 有最大乘积 6。
    示例 2:

    输入: [-2,0,-1]
    输出: 0
    解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

    思路:与连续最大和思路类似,但这里需要注意乘积计算遇到负数的情况

    动态数组dp[i]表示,以第i个元素最为结尾的子数组,连续最大乘积是多少;

    遍历数组时计算当前最大值,不断更新;


    令imax为当前最大值,则当前最大值为 imax = max(imax * nums[i], nums[i])
    由于存在负数,那么会导致最大的变最小的,最小的变最大的。因此还需要维护当前最小值imin,
    imin = min(imin * nums[i], nums[i])
    当负数出现时则imax与imin进行交换再进行下一步计算
    时间复杂度:O(n)

    class Solution {
    public:
        int maxProduct(vector<int>& nums) {
            int len= nums.size();
            if (len ==1){
                return nums[0];
            }
            vector<int> dp(len,0);
            dp[0] = nums[0];
            int res = dp[0];
            int imax = dp[0];
            int imin = dp[0];        
    
            for (int i=1;i<len;i++){
                if(nums[i]<0){
                    int temp = imax;
                    imax = imin;
                    imin = temp;
                }
                imax = max(imax*nums[i],nums[i]);
                imin = min(imin*nums[i],nums[i]);
    
                res = max(res,imax);
            }
    
            return res;
        }
    };
    

      

    6、最小路径和:

    给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

    说明:每次只能向下或者向右移动一步。

    示例:

    输入:
    [
      [1,3,1],
    [1,5,1],
    [4,2,1]
    ]
    输出: 7
    解释: 因为路径 1→3→1→1→1 的总和最小。

    思路 : 应该用动态规划思路(贪心?)
    dp[i][j]表示到i,j表示的单元格,最小和
    dp[i][j] 的值只和上面的数和左边的数有关系,取两种路径中最小的即可
    第一行,第一列特殊处理,然后,根据给的样例,写出递推矩阵

    1,4,5
    2,7,6
    6,8,7

    一般化:dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j];

    遇到这种问题,可以动动手,大致演算一下,找到其中的状态转移方程,本题一次可以AC

    class Solution {
    public:
        int minPathSum(vector<vector<int>>& grid) {
        	int row = grid.size();
        	int col = grid[0].size();
    
        	vector<vector<int>> dp(row,vector<int>(col,0));
    
        	int sum = 0;
        	for(int i=0;i<row;i++){
        		dp[i][0]=sum+grid[i][0];
        		sum = sum+grid[i][0];
        	}
    
        	sum = 0;
        	for(int j=0;j<col;j++){
        		dp[0][j]=sum+grid[0][j];
        		sum = sum+grid[0][j];
        	}
    
        	for(int i= 1;i<row;i++){
        		for(int j=1;j<col;j++){
        			dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
        		}
        	}
    
        	return dp[row-1][col-1];
    
        }
    };
    

      

    7、零钱兑换

    给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

    示例 1:

    输入: coins = [1, 2, 5], amount = 11
    输出: 3 
    解释: 11 = 5 + 5 + 1
    示例 2:

    输入: coins = [2], amount = 3
    输出: -1
     

    说明:
    你可以认为每种硬币的数量是无限的。

    思路(动态规划):一般动动手,把例子中的结果递推出来,然后抽象成状态转移方程,再写代码:

    动态规划解题套路框架:
    1)先确定「状态」,也就是原问题和子问题中变化的变量。由于硬币数量无 限,所以唯一的状态就是目标金额 amount 。
    2)然后确定 dp 函数的定义:当前的目标金额是 n ,至少需要 dp(n) 个硬 币凑出该金额。
    3)然后确定「选择」并择优,也就是对于每个状态,可以做出什么选择改变当 前状态。具体到这个问题,无论当的目标金额是多少,选择就是从面额列表
    coins 中选择一个硬币,然后目标金额就会减少:

    class Solution {
    public:
        int coinChange(vector<int>& coins, int amount) {
    
        	// 初始化动态数组,为一个比较大的初始值
        	vector<int> dp(amount+1,amount+999);
    
        	if(amount == 0){
        		return 0;
        	}
    
        	if(amount <0){
        		return -1;
        	}
    
        	dp[0]=0;
        	//i代表要凑出的金额
        	for(int i=0;i<dp.size();i++){
        		for(int coin:coins){
                    // 判断能否加入
        			if(i-coin<0) continue;
                    // 状态变化情况
        			dp[i]=min(dp[i],dp[i-coin]+1);
        		}
        	}
    
        	if(dp[amount]==amount+999){
        		return -1;
        	}
    
        	return dp[amount];
    
        }
    };
    

      

    零钱兑换II:
    给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

    思路:
    我们可以把这个问题转化为背包问题的描述形式:
    int change(int amount, int[] coins);
    经典动态规划:完全背包问题
    有一个背包,最大容量为 amount ,有一系列物品 coins ,每个物品的重 量为 coins[i] ,每个物品的数量无限。请问有多少种方法,能够把背包恰 好装满?
    这个问题和我们前面讲过的两个背包问题,有一个最大的区别就是,每个物 品的数量是无限的,这也就是传说中的「完全背包问题」,没啥高大上的, 无非就是状态转移方程有一点变化而已。
    下面就以背包问题的描述形式,继续按照流程来分析。
    解题思路 第一步要明确两点,「状态」和「选择」。
    状态有两个,就是「背包的容量」和「可选择的物品」,选择就是「装进背
    包」或者「不装进背包」嘛,背包问题的套路都是这样。
    明白了状态和选择,动态规划问题基本上就解决了,只要往这个框架套就完
    事儿了:
    第二步要明确 dp 数组的定义。 首先看看刚才找到的「状态」,有两个,也就是说我们需要一个二维 dp
    数组。
    dp[i][j] 的定义如下:
    若只使用前 i 个物品,当背包容量为 j 时,有 dp[i][j] 种方法可以装 满背包。
    换句话说,翻译回我们题目的意思就是:
    for 状态1 in 状态1的所有取值:
    for 状态2 in 状态2的所有取值:
    for ...
    dp[状态1][状态2][...] = 计算(选择1,选择2...)
    152

    经典动态规划:完全背包问题
    若只使用 coins 中的前 i 个硬币的面值,若想凑出金额 j ,有 dp[i] [j] 种凑法。
    经过以上的定义,可以得到:
    basecase为 dp[0][..] = 0, dp[..][0] = 1 。因为如果不使用任何硬币面 值,就无法凑出任何金额;如果凑出的目标金额为 0,那么“无为而治”就是 唯一的一种凑法。
    我们最终想得到的答案就是 dp[N][amount] ,其中 N 为 coins 数组的大 小。
    大致的伪码思路如下:
    int dp[N+1][amount+1] dp[0][..] = 0 dp[..][0] = 1
    for i in [1..N]:
    for j in [1..amount]:
    把物品 i 装进背包,
    不把物品 i 装进背包 return dp[N][amount]
    第三步,根据「选择」,思考状态转移的逻辑。 注意,我们这个问题的特殊点在于物品的数量是无限的,所以这里和之前写
    的背包问题文章有所不同。
    如果你不把这第 i 个物品装入背包,也就是说你不使用 coins[i] 这个面 值的硬币,那么凑出面额 j 的方法数 dp[i][j] 应该等于 dp[i-1][j] , 继承之前的结果。
    如果你把这第 i 个物品装入了背包,也就是说你使用 coins[i] 这个面值 的硬币,那么 dp[i][j] 应该等于 dp[i][j-coins[i-1]] 。
    首先由于 i 是从 1 开始的,所以 coins 的索引是 i-1 时表示第 i 个硬 币的面值。

    经典动态规划:完全背包问题
    dp[i][j-coins[i-1]] 也不难理解,如果你决定使用这个面值的硬币,那么 就应该关注如何凑出金额 j - coins[i-1] 。
    比如说,你想用面值为 2 的硬币凑出金额 5,那么如果你知道了凑出金额 3 的方法,再加上一枚面额为 2 的硬币,不就可以凑出 5 了嘛。
    综上就是两种选择,而我们想求的 dp[i][j] 是「共有多少种凑法」,所以 dp[i][j] 的值应该是以上两种选择的结果之和:

    class Solution {
    public:
        int change(int amount, vector<int>& coins) {
    
        	int len = coins.size();
        	vector<vector<int>> dp(len+1,vector<int>(amount+1));
    
        	// 初始化状态
        	for (int i = 0; i <= len; i++){
        		dp[i][0] = 1;
        	}
    
        	for (int i = 1; i <= len; i++) {
        		for (int j = 1; j <= amount; j++){
        			if (j - coins[i-1] >= 0){
        				dp[i][j] = dp[i - 1][j]+ dp[i][j - coins[i-1]]; 
        			}else{
        				dp[i][j] = dp[i - 1][j];
        			}
        		}
        	}
    
        	return dp[len][amount];
    
        }
    };
    

      

    而且,我们通过观察可以发现, dp 数组的转移只和 dp[i][..] 和 dp[i- 1][..] 有关,所以可以压缩状态,进一步降低算法的空间复杂度:
    这个解法和之前的思路完全相同,将二维 dp 数组压缩为一维,时间复杂 度 O(N*amount),空间复杂度 O(amount)。

    解法一中,会造成大量的重复计算,因此我们通过缓存结果来提高性能。通过解法一种,我们知道,整个组合数就是:

    • 对每种硬币类型只有选和不选两种结果
    • 选择后 amount = amount -(选择的硬币面额)
    class Solution {
    public:
        int change(int amount, vector<int>& coins) {
    
        	int len = coins.size();
    
        	vector<int> dp(amount+1);
    
        	dp[0] = 1;
    
        	for(int i=0;i<len;i++){
        		for(int j=1;j<=amount;j++){
        			if(j-coins[i]>=0){
        				dp[j]=dp[j]+dp[j-coins[i]];
        			}
        		}
        	}
    
        	return dp[amount];
    
    
        }
    };
    

      

    121. 买卖股票的最佳时机
    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

    如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

    注意:你不能在买入股票前卖出股票。

    示例 1:

    输入: [7,1,5,3,6,4]
    输出: 5
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
    注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
    示例 2:

    输入: [7,6,4,3,1]
    输出: 0
    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。


    思路:
    1)常规思路,元素两两做差求最大值,差小于0则置为0,复杂度为o(n*n)
    2)动态规划思路,dp[i]表示在第i天卖出的最大收益,就等于第i-1天不卖的最大收益,加上第i天-第i-1天的价格差
    dp[i]=max(0,dp[i-1]+prices[i]-prices[i-1]) , 复杂度为o(n)

    class Solution {
    public:
        int maxProfit(vector<int>& prices) {
    
            int len = prices.size();
            vector<int> dp(len+1,0);
    
            dp[0]=0;
    
            for(int i=1;i<len; i++){
                dp[i]=max(0,dp[i-1]+prices[i]-prices[i-1]);
            }
            int max = 0;
            for(auto x:dp){
                if(x>max){
                    max = x;
                }
            }
    
            return max;
    
        }
    };
    

      

    62、不同路径
    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

    机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

    问总共有多少条不同的路径?


    思路:这个动态规划题目很简单:

    * * *
    * * *
    * * *

    左上角到右下角:
    1 1 1
    1 2 3
    1 3 6


    根据输入矩阵大小,例举出不同路径的个数,得到状态转移方程
    dp[i][j]=dp[i][j-1]+dp[i-1][j]
    解释为:到[i,j]的路径数为,到上面的点[i-1][j]的路径数,加上到左边的点[i][j-1]的路径数,加上到左边的点

    class Solution {
    public:
        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][j-1]+dp[i-1][j];
                }
            }
    
            return dp[m-1][n-1];
        }
    };
    

      

    70、爬楼梯:
    假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

    每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

    注意:给定 n 是一个正整数。

    示例 1:

    输入: 2
    输出: 2
    解释: 有两种方法可以爬到楼顶。
    1. 1 阶 + 1 阶
    2. 2 阶
    示例 2:

    输入: 3
    输出: 3
    解释: 有三种方法可以爬到楼顶。
    1. 1 阶 + 1 阶 + 1 阶
    2. 1 阶 + 2 阶
    3. 2 阶 + 1 阶


    4
    1--1--1--1
    1--2--1
    1--1--2
    2--1--1
    2--2


    思路:也很简单,爬到第n阶的路径为爬到第n-1阶级的路径加上爬到第n-2阶的路径个数

    所以状态转移方程:
    dp[n]=dp[n-1]+dp[n-2]

    dp[1]=1
    dp[2]=2

    class Solution {
    public:
        int climbStairs(int n) {
            if(n==1){
                return 1;
            }
            if(n==2){
                return 2;
            }
    
            vector<int> dp(n);
            dp[0]=1;
            dp[1]=2;
    
            for(int i=2;i<n;i++){
                dp[i]=dp[i-1]+dp[i-2];
            }
    
            return dp[n-1];
    
        }
    };
    

      

    18. 杨辉三角
    给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。

    思路:按照输出,写递推方程即可,基本一次AC
    考虑输入行数为,0,1,2的特殊情况即可


    [
    [1],
    [1,1],
    [1,2,1],
    [1,3,3,1],
    [1,4,6,4,1]
    ]

    class Solution {
    public:
        vector<vector<int>> generate(int numRows) {
            if(numRows==0){
                return {};
            }
    
            if(numRows==1){
                return {{1}};
            }
    
            if(numRows==2){
                return {{1},{1,1}};
            }
    
            vector<vector<int>> res;
            vector<int> vec1={1};
            vector<int> vec2={1,1};
    
            res.push_back(vec1);
            res.push_back(vec2);
    
            for(int i=2;i<numRows;i++){
                vector<int> temp;
                temp.push_back(1);
                for(int j=1;j<i;j++){
                    temp.push_back(res[i-1][j-1]+res[i-1][j]);
                }
                temp.push_back(1);
                res.push_back(temp);
            }
            return res;
        }
    };
    

      

    1277. 统计全为 1 的正方形子矩阵
    给你一个 m * n 的矩阵,矩阵中的元素不是 0 就是 1,请你统计并返回其中完全由 1 组成的 正方形 子矩阵的个数。

    示例 1:

    输入:matrix =
    [
    [0,1,1,1],
    [1,1,1,1],
    [0,1,1,1]
    ]

    0 1 1 1
    1 1 2 2
    0 1 2 3

    求和后,为15

    输出:15
    解释:
    边长为 1 的正方形有 10 个。
    边长为 2 的正方形有 4 个。
    边长为 3 的正方形有 1 个。
    正方形的总数 = 10 + 4 + 1 = 15.
    示例 2:

    输入:matrix =
    [
    [1,0,1],
    [1,1,0],
    [1,1,0]
    ]

    1 0 1
    1 1 0
    1 2 0

    求和后为7


    输出:7
    解释:
    边长为 1 的正方形有 6 个。
    边长为 2 的正方形有 1 个。
    正方形的总数 = 6 + 1 = 7.

    思路:还是动态规划思路,通过列举结果矩阵,找到一般化的规律。
    在这里,创一个dp数组等于matrix,每个点比较其左边、上边和左上三个点的关系, 动态方程为:
    dp[i][j]=min(dp[i-1][j],dp[i-1][j-1],dp[i][j-1])+1
    dp[i][j]定义为,以i,j 为右下角的正方形的数目;
    所以,在计算出所有的 dp[i][j] 后,我们将它们进行累加,就可以得到矩阵中正方形的数目。

    https://leetcode-cn.com/problems/count-square-submatrices-with-all-ones/solution/tong-ji-quan-wei-1-de-zheng-fang-xing-zi-ju-zhen-2/

    class Solution {
    public:
        int countSquares(vector<vector<int>>& matrix) {
            if(matrix.size()==0) return 0;
            int m = matrix.size();
            int n = matrix[0].size();
            int ans = 0;
            vector<vector<int>> dp(m, vector<int>(n, 0));
    
    
            for(int i=0;i<m;i++)
            {
                for(int j=0;j<n;j++)
                {
                    if(matrix[i][j])
                    {
                        if(i==0 || j==0){
                            dp[i][j] = 1;
                        }
                        else{
                            dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
                        }
                        ans += dp[i][j];
                    }
                }
            }
            return ans;
        }
    };
    

      

  • 相关阅读:
    在 Eclipse Workbench 之外使用 Eclipse GUI
    GB2312,GBK,Unicode
    木偶一之推荐系统
    Matlab:任意矩阵计算分布密度(海明距离的分布密度)
    live555在arm linux下的交叉编译,并下载的Arm板播放H264文件
    java设计模式之原型模式
    HDU 1102
    poj3661另一种做法(滚动数组)
    基于QT的小游戏细菌病毒战
    某代码查看器的保护突破
  • 原文地址:https://www.cnblogs.com/Allen-rg/p/13679827.html
Copyright © 2011-2022 走看看