动态规划 LeetCode 70&120&64&279&300&322

LeetCode 70 爬楼梯

题目

假设你正在爬楼梯。需要 n阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

思路

F(n)=F(n-1)+F(n-2) 斐波那契数列的应用

代码

#include <iostream>
#include <vector>
using namespace std;

//动态规划解 
class Solution {
public:
    int climbStairs(int n) {
    	vector<int> memo;
		memo = vector<int>(n+1,-1);
		memo[1] = 1;
		memo[2] = 2;
		for(int i=3; i<=n; i++)
			memo[i] = memo[i-1] + memo[i-2];
		return memo[n]; 
    }
};
int main()
{
	return 0;
}

LeetCode 120 三角形最小路径和

题目

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
例如,给定三角形:
[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]
自顶向下的最小路径和为11(即,2+3+5+1= 11)。

思路

从倒二行起,从下至上,从左至右,依次更新三角形数组上的值

更新值为下一层相邻两个数和的最小值。那么triangle[i][j]就表示从点[(i,j)开始自顶向下的最小路径和

代码

#include <iostream>
#include <vector>
using namespace std;

//动态规划解 
class Solution {
public:
    int minimumTotal(vector<vector<int> >& triangle) {
    	int n = triangle.size();
    	if(n==0) return 0;
    	//从倒二行开始更新数组,数组更新值应该等于该数与其下一层相邻两个数和的最小值 
		for(int i=n-2; i>=0; i--)
			for( int j=0; j<triangle[i].size(); j++)
				triangle[i][j] = min(triangle[i][j]+triangle[i+1][j],triangle[i][j]+triangle[i+1][j+1]);
		return triangle[0][0];
    }
};
int main()
{
	return 0;
}

LeetCode 64 最小路径和

题目
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

思路

从右至左,从底至上更新grid的每个值
grid的值代表的是该点移动到右下角点的最小路径和

如果是最后一行,只能向右移动,grid[i][j] = grid[i][j]+grid[i][j+1]

如果是最后一列,只能向下移动,grid[i][j] = grid[i][j]+grid[i+1][j]

当可以向下走或者向右走,寻找最短路径:grid[i][j] = min(grid[i][j]+grid[i][j+1],grid[i][j]+grid[i+1][j]);

代码

//动态规划解 
class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
    	//思路:从右至左,从底至上更新grid的每个值
		//grid的值代表的是该点移动到右下角点的最小路径和
		int n = grid.size();
		if(n==0) return 0;
		int m = grid[0].size();
		
		for(int i=n-1; i>=0; i--) //从最后一行开始更新 
		{
			for(int j=m-1; j>=0;j--) //从最后一列开始更新
			{
				//判断当前数是否可以向右向下走
				if(i==n-1 && j==m-1)
					continue;
				else if(i==n-1) //最后一行,只能向右走
					grid[i][j] += grid[i][j+1];
				else if(j==m-1) //最后一列,只能向下走
					grid[i][j] += grid[i+1][j];
				else //可以向下,也可以向右走,寻找最短路径
					grid[i][j] = min(grid[i][j]+grid[i][j+1],grid[i][j]+grid[i+1][j]);
					 
			} 
		} 
		return grid[0][0];
    }
};

LeetCode 279 完全平方数

题目
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。
你需要让组成和的完全平方数的个数最少。

示例 1:
输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.

思路

使用memo数组保存组成和为i的完全平方数的最少个数。

想要计算每个memo[i],找到所有0,i之间的完全平方数x memo[i]=min(1+memo[i-x])

代码

#include <iostream>
#include <vector>
using namespace std;

//动态规划解 
class Solution {
public:
    int numSquares(int n) {
		vector<int> memo(n+1,0); //memo中存着组成n和的完全平方数的最少个数。
		memo[1] = 1; 
		//获取每个memo[i] 
		for(int i=2; i<=n; i++)
		{
			memo[i] = 1+memo[i-1]; //第一种划分方式是划分成1和 i-1(i-1继续划分) 
			for(int j=1; i-j*j>=0 ; j++) //将i划分为 j*j  i-j*j 
			{
				if(i-j*j==0) //说明这个数是完全平方数 
				{
					memo[i]=1;
					continue;	
				}
				memo[i] =min(memo[i],1+ memo[i-j*j]); 
			}
		} 
		return memo[n];
    }
};
int main()
{
	return 0;
}

LeetCode 300 最长上升子序列

题目
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。

思路

使用数组memo:memo[i]中存储的是以nums[i]结尾的最长上升子序列长度

初始化:每个只有自身的上升子序列长度都为1

对于当前的数字nums[i],遍历nums[i]前面所有的数,找到一个比nums[i]小的数,说明nums[i]可以跟在找到的数后面 
memo[i]=找到所有可能nums[i]能跟在后面的值j memo[j]的最大值+1

代码

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
		int n=nums.size();
		if(n==0) return 0;
		
		vector<int> memo(n,1);//memo[i]中存储的是以nums[i]结尾的最长上升子序列长度
		
		for(int i=1; i<n; i++)
		{
			//遍历nums[i]前面所有的数,找到一个比nums[i]小的数,nums[i]可以跟在找到的数后面 
			//找到所有可能结果的最大值 
			for(int j=0; j<i; j++) 
			{
				if(nums[i] > nums[j])
					memo[i] = max(memo[i],memo[j]+1); 
			}
		} 
		
		int res = 0;
		for(int i=0; i<n; i++)
			res = max(res,memo[i]);
		return res;
    }
};

LeetCode 322

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

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

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
    	int Max = amount + 1;
		int n = coins.size();
		if(n==0) return -1;
		if(amount==0) return 0;
		//凑成i所需的少的硬币个数。 
		vector<int> memo(amount+1,Max);
		memo[0] = 0;
		for(int i=0; i<=amount; i++)
		{
			//对于当前金额i memo[i]=min(memo[i-coins[j]+1) 
			for(int j=0; j<n; j++)
			{
				if(i>=coins[j])
				{
					memo[i] = min(memo[i],memo[i-coins[j]] + 1);
				}
			} 
		} 
		return memo[amount]>amount?-1:memo[amount];
    }
};

洛谷上的一道经典动态规划问题 过河卒

思路

使用一个memo数组,存储从座标(i,j)到达B点的路径条数

如果一个点是马所在的点或者马的控制点,那么该点无法到达B,memo[i][j]=0

对于每一个不是马所在的点或者马的控制点,从下至上,从右至左依次计算memo[i][j]:

如果点(i,j)是最后一行的点,那么只能往右走,memo[i][j]=memo[i][j+1]

如果点(i,j)是最后一列的点,那么只能往下走,memo[i][j]=memo[i+1][j]

其他点,可以向下或者向右走,memo[i][j] = memo[i+1][j]+memo[i][j+1];

代码

bool inArea(int x, int y, int Bx, int By )
{
	return x>=0 && y>=0 && x<=Bx && y<=By;
}
int main()
{
	int Bx,By;
	int Mx,My;
	cin>>Bx>>By>>Mx>>My;
	vector<vector<long long> > memo(Bx+1,vector<long long>(By+1,-1));
	
	//先将马所占位置置为0
	memo[Mx][My] = 0;
	for(int i= Mx-2; i<=Mx+2; i++)
	{
		if(i==Mx) continue;
		if(i==Mx-2 || i==Mx+2 )
		{
			if(inArea(i,My-1,Bx,By))
				memo[i][My-1] = 0;
			if(inArea(i,My+1,Bx,By))
				memo[i][My+1] = 0;
		}else
		{
			if(inArea(i,My-2,Bx,By))
				memo[i][My-2] = 0;
			if(inArea(i,My+2,Bx,By))
				memo[i][My+2] = 0;
		}
	} 
	//先算出最后一行的全部解
	memo[Bx][By] = 1;
	for(int i=By-1; i>=0; i--)
	{
		if(memo[Bx][i]!=0) 
			memo[Bx][i] = memo[Bx][i+1];
	}
	//遍历每一行 
	for(int i=Bx-1; i>=0; i--)
	{
		for(int j=By; j>=0; j--)
		{
			if(memo[i][j]!=0)
			{
				if(j==By)
				{
					memo[i][j] = memo[i+1][j];
				}else
				{
					memo[i][j] = memo[i+1][j]+memo[i][j+1];
				}
			}
		}
	} 	
	cout<<memo[0][0]<<endl;	
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章