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;
}