Leetcode刷题 9

动态规划:
第一题:爬楼梯 leetcode 70
在爬楼梯时,每次可向上走1阶台阶或2阶台阶,问有n阶楼梯有多少种上楼的方式?

思考:用常规得到暴力搜索肯定时间通不过,故我们分析。由于每次最多爬2阶,楼梯的第i阶,只可能从楼梯的第i-1阶与第i-2阶到达,故到达第i阶有多少种爬法,只与第i-1阶、第i-2阶的爬法数量直接相关。所以第i阶的爬法数量=第i-1阶爬法数量+第i-2阶爬法数量。
1.设置地推数组dp[0…n], dp[i]代表到达第i阶,有多少种走法,初始化数组元素为0
2.设置到达第1阶台阶,有1种走法;到达第2阶台阶,有2种走法。
3.利用i循环递归从第3阶至第n阶结果:
到达第i阶的方式数量 = 到达第i-1阶的方式数量+到达第i-2阶的方式数量
在这里插入图片描述

#include <vector>
int climbStairs(int n){
	std::vector<int> dp(n+3,0);
	dp[1]=1;
	dp[2]=2;
	for(int i=3;i<n;i++)
	 dp[i]=dp[i-1]+dp[i-2];
 return dp[n];
}

动态规划有四个步骤:
1.确认原问题与子问题;
2.确认状态:
3.确认边界状态的值:
4.确定状态转移方程:

第二题:打家劫舍 leetcode 198
在一条直线上,有n个房屋,每个房屋中有数量不等的财宝,有一个盗贼希望从房屋中盗取财宝,由于房屋中有报警器,如果同时从相邻的两个房屋中盗取财宝就会触发报警器。问在不触发报警器的前提下,最多可获取多少财宝?

在这里插入图片描述
思考:由于同时从相邻的两个房屋中盗取财宝就会触发报警器,故:
a.若选择第i个房间盗取财宝,就一定不能选择第i-1房间个房间盗取财宝;
b.若不选择第i个房间盗取财宝,则相当于只考虑前i-1个房间盗取财宝。

在这里插入图片描述
1.确认原问题与子问题:
原问题:求n个房间的最优解
子问题:求前1个房间、前2个房间、、、前n-1个房间的最优解

2.确认状态:
第i个状态即为前i个房间能够获得的最大财宝(最优解)

3.确认边界状态的值:
前1个房间的最优解,第1个房间的财宝;
前2个房间的最优解,第1、2个房间中较大财宝的。

4.确定状态转移方程:
a.选择第i个房间:第i个房间+前i-2个房间的最优解
b.不选择第i个房间:前i-1个房间的最优解
动态规划转移方程:
dp[i]=max(dp[i-1],dp[i-2]+nums[i]) ; i>=3

  int rob(vector<int>& nums){
		if(nums.size()==0)
		   return 0;
		  if(nums.size()==1)  
		  return nums[0];
		 vector<int> dp(nums.size(),0);
		 dp[0]=nums[0];
		 dp[1] = max(nums[0],nums[1]);
		 for(int i=0i<nums.size();i++)
		 	dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
    return dp[nums.size()-1];
	}

第三题:最大子段和:Leetcode 53
给定一个数组,求这个数组的连续子数组中,最大的那一段的和。
如数组[-2,1,-3,4,-1,2,1,-5,4];
连续子数组:[4,-1,2,1]为6

思路:将n个数的数组的最大子段和,转换为分别求出以第一个、第二个、、、、、第i个、、、第n个数字结尾的最大子段和,再找出这n个结果中最大的,即为结果。
动态规划算法:
第i个状态(dp[i])即为以第i个数字结尾的最大子段和(最优解)。由于以第i-1个数字结尾的最大子段和(dp[i-1])与nums[i]相邻:
在这里插入图片描述

 int maxSubArray(vector<int>& nums){
	vector<int> dp(nums.size(),0);
	dp[0] = nums[0];
	int des_max=dp[0];
	for(int i=1;i<nums.size();i++){
		dp[i] = max(dp[i-1]+nums[i],nums[i]);
		if(des_max<dp[i])
				des_max=dp[i];
	}
	return des_max;

}

第四题:找零钱 leetcode 322
已知不同面值的钞票,求如何用最少数量的钞票组成某个金额,求可以使用的最少钞票数量。如果任意数量的已知面值钞票都无法组成该金额,则返回-1.
例如:
钞票面值:[1,2,5]; 金额:11=5+5+1 ; 需要3张

思路:
钞票面值:coins=[1,2,5,7,10];金额:14
dp[i],代表金额i的最优解(即最小使用张数)
数组dp[]中中存储金额1至金额14的最优解(最少使用钞票的数量)

在计算dp[i]时,dp[0]、dp[1]、dp[2]、、、dp[i-1]都是已知的:
而金额i可由:
金额i-1与coins0组合
金额i-2与coins1组合
金额i-5与coins2组合
金额i-7与coins3组合
金额i-10与coins4组合

即状态i可由状态i-1、i-2、i-5、i-7、i-10转移到

在这里插入图片描述
在这里插入图片描述

int coinChange(vector<int> & coins,int amount){
		vector<int> dp;
		for(int i=0;i<=amount;i++)
			dp.push_back(-1);
		dp[0]=coins[0];
		for(int i=1;i<=amount;i++){
			for(int j=0;j<coins.size();j++){
					if(i-conis[j]>0 && dp[i-coins[j]]!=-1){
							if(dp[i]==-1||dp[i]>dp[i-coins[j]]+1)
								dp[i]=dp[i-conis[j]]+1;
					}
			}
		}
		return dp[amount];
	}

第五题:三角形 Leetcode 120
给定一个二维数组,其保存了一个数字三角形,求从数字三角形顶端到底端各数字和最小的路径之和,每次可以向下走相邻的两个位置。
在这里插入图片描述

思考:从上到下或者从下导上的寻找路径的思考方式本质是一样的嘛?
假设dp[i][j]代表了数组三角形第i行、第j列的最优解,从上到下与下到上哪种方式递推更容易?(更少的考虑边界条件)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

算法思路:
1、设置一个二维数组,最优值三角形dp[][],并初始化数组元素为0.dp[i][j]代表了从底向上递推时,走到三角形第i行第j列的最优解。
2、从三角形的底面向三角形上方进行动态规划:
a.动态规划边界条件:底面上的最优值即为数字三角形的最后一层。
b.利用i循环,从倒数第二层递推到第一层,对于每层的各列,进行动态规划递推:
第i行,第j列的最优解为dp[i][j],可到达(i,j)的两个位置的最优解dp[i+1][j]、dp[i+1][j+1]::
dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j]
3.返回dp[0][0]
在这里插入图片描述

int minimumTotal(vector<vector<int>>& triangle){
	if(triangle.size()==0) return 0;
	vector<vector<int>> dp;
	for(int i=0;i<triangle.size();i++){
		dp.push_back(vector<int>());
		for(int j=0;j<triangle.size();j++)
			dp[i].push_back(0);
		}
		for(int i=0;i<dp.size();i++)
		 dp[dp.size()-1][i]=triangle[dp.size()-1][i]; 
     for(int i=dp.size()-2;i>=0;i--){
		for(int j=0;j<dp[i].size();j++){
					do[i]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j];
			}
	}
	return dp[0][0];
}

第六题:最长上升子序列
已知一个未排序的数组,求这个数组最长上升子序列的长度。
例如:[1,3,2,3,1,4],其中最长的上升子序列长度为4。分别考虑O(n^2)与O(nlogn)两种复杂度算法
动态规划:
1.第i个状态代表前i个数字中最长上升子序列的长度,是否可找出dp[i]与dp[i-1]的关系?
2.若第i个状态代表以第i个数字为结尾的最长上升子序列的长度,是否可找出dp[i]与dp[i-1]的关系?再如何求出n个数字的最长上升子序列?
[1,3,2,3,1,4]
若第i个状态dp[i]代表以第i个元素结尾的最长上升子序列的长度:
dp[i-1]代表以第i-1个元素结尾的最长上升子序列长度,
nums[i]一定是dp[i]所对应的最长上升子序列中的最大元素(因为在末尾)
在这里插入图片描述
思路:设置动态规划数组dp[],第i个状态dp[i]代表以第i个元素结尾的最长上升子序列的长度:动态规划边界:dp[0]=1;
初始化最长上升子序列的长度LIS=1;
从1到n-1,循环i,计算dp[i]:
从0至i-1,循环j,若nums[i]>nums[j],说明nums[i]可放置在nums[j]的后面,组成最长上升子序列:
若dp[i]<dp[j]+1:
dp[i]=dp[j]+1
LIS为dp[0],dp[1],dp[i],dp[n-1]中最大的
在这里插入图片描述

int LengthOfLIS(vector<int>& nums){
	if(nums.size()==0) return 0;
	int LIS=1;
	vector<int> dp(nums.size(),0);
	dp[0]=1;
	for(int i=1;i<dp.size();i++){
		dp[i]=1;
		for(int j=0;j<i;j++)
		{
				if(nums[i]>nums[j] && dp[i]<dp[j]+1)
						dp[i]=dp[j]+1;
		}
		if(LIS<dp[i])
		LIS=dp[i];
	}
	return LIS;
}

思路2:
设置一个栈(使用vector实现)stack,stack[i]代表长度为i+1的上升子序列最后一个元素的最小可能取值,即若要组成长度为i+2的上升子序列,需要一个大于stack[i]的元素。最终栈的大小,即为最长上升子序列的长度。
在这里插入图片描述
nums=[1,3,2,3,1,4]
1.设置一个栈(使用vector实现),将nums[0]push栈中。
2.从1至n-1遍历nums数组:
若nums[i]>栈顶:将nums[i]push至栈中。
否则:
从栈底遍历至栈顶,若遍历时,栈中元素大于等于nums[i],
使用nums[i]替换该元素,并跳出循环。
3.返回栈的大小

int LengthOfLIS(vector<int>& nums){
		if(nums.size()==0) return 0;
		vector<int> stack;
		stack.push(nums[0]);
		for(int i=0;i<nums.size();i++){
			if(nums[i]>stack.back())
			  stack.push_back(nums[i]);
			else {
						for(int j=0;j<stack.size();j++)
						{
							if(stack[j]>=nums[i])
							stack[j]=nums[i];
						}
				}
	}
	return stack.size();
}

第七题:最小路径和
已知一个二维数组,其中存储了非负整数,找到从左上角到右下角的一条路径,使得路径上的和最小。(移动过程中只能向下或向右)
Leetcode 64

思考:设dp[i][j]为到达位置(i,j)时的最优解(最小值):
dp[i][j]与dp[i-1][j]、dp[i][j-1]、grid[i][j]之间的关系是什么?
在这里插入图片描述

int minPathSum(vector<vector<int>> & grid){
	if(grid.size()==0)
		return 0;
	int row = grid.size();
	int col = grid[0].size();
	vector<vector<int>> dp(row,vector<int>(col,0));
	dp[0][0]=grid[0][0];
	for(int i=1;i<col;i++)
	 dp[0][i] = dp[0][i-1]+grid[0][i];
	 for(int i=1;i<row;i++){
		dp[i][0] = dp[i-1][0]+grid[i][0];
		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];
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值