【动态规划概述】
动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程最优化的数学方法。
它是20世纪50年代初美国数学家R.E.Bellman等人提出的最优化原理,它利用各阶段之间的关系,
逐个求解,最终求得全局最优解。
1.【确认原问题与子问题】
2.【动态规划状态】
3.【边界状态的值】
4.【状态转移方程】
一,爬楼梯
1.数组的初始化,大小是函数的参数?
用变量定义数组长度时,不可以初始化
但是vector可以vector< dp >( size,value)
2.子数组连续,可以递归
3. 到达第i阶的方式数量 = 到达第i-1阶的方式数量 + 到达第i-2阶的方式数量
4.状态单一
class Solution {
public:
int climbStairs(int n) {
// int dp[n]={0};//dp[n]不行
vector<int>dp(n+3,0);//dp(n,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];
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/climbing-stairs/solution/pa-lou-ti-by-xia-mu-lao-zhang-ren-pw01/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二,打家劫舍
1.连续子数组,可以递归
2.状态方程的提取
3.状态不单一
class Solution {
public:
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=2;i<nums.size();i++){
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.size()-1];
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/house-robber/solution/da-jia-jie-she-by-xia-mu-lao-zhang-ren-clpi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
三,最大子序和
方法一,动态规划
1.dp[i]代表【以第i个元素结尾】的最长递增子序列,之前是从头开始
2.更新dp[i]条件(状态转移方程)
3.子数组不连续,不能直接递归,和前两个不一样
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0){
return 0;
}
vector<int>dp(nums.size(),0);//dp[i]代表以第i个元素结尾的最长递增子序列
dp[0]=1;//边界
int LIS=1;
for(int i=1;i<dp.size();i++){
dp[i]=1;//单个元素最小长度是1
for(int j=0;j<i;j++){
if(nums[i]>nums[j]&&dp[i]<dp[j]+1){//后面的数比前面的数大,且当前状态长度比后面状态长度+1小,更新dp【i】
dp[i]=dp[j]+1;
}
}
if(LIS<dp[i]){
LIS=dp[i];
}
}
return LIS;
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-di-zeng-zi-xu-lie-by-xia-mu-la-ukwd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法二,栈
1.用vector实现栈
2.优化查找
四,找零钱
1.状态转移【枚举所有和dp[i]有关的状态】【状态不单一】
2.更新最优解【状态转移方程】
3.前一状态是否可以达到
4.vector的初始化【为什么会溢出】
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
//vector<int>dp(amount,-1);//为啥会溢出啊?????????
vector<int>dp;
for(int i=0;i<=amount;i++){
dp.push_back(-1);
}
dp[0]=0;//边界
for(int i=1;i<=amount;i++){
for(int j=0;j<coins.size();j++){//dp[i]代表达到金额i所需要的最小张数
if(i-coins[j]>=0&&dp[i-coins[j]]!=-1){//当前金额包含面值,或上一状态已达到
if(dp[i]==-1||dp[i]>dp[i-coins[j]]+1){//若当前状态未达到,或所需张数更小,则更新
dp[i]=dp[i-coins[j]]+1;
}
}
}
}
return dp[amount];
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/coin-change/solution/ling-qian-dui-huan-by-xia-mu-lao-zhang-r-v9ty/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
五,三角形
1.二维dp
2.自上而下
3.涉及考虑边界
3.从下往上,更少考虑边界,【反向思维】
4.无效化【不理解】
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
if(triangle.empty()||triangle[0].empty()){
return 0;
}
int m=triangle.size();
int dp[m][m];//初始化状态转移矩阵,表示从(i,j)到底部的最小路径和
for(int i=0;i<m;i++){
dp[m-1][i]=triangle[m-1][i];//最低行个节点的最小路径为节点本身
}
for(int i=m-2;i>=0;i--){//填充状态转移矩阵
for(int j=0;j<i+1;j++){
dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j];
}
}
return dp[0][0];//即为题解
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/triangle/solution/san-jiao-xing-zui-xiao-lu-jing-he-by-xia-gmn7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int m=triangle.size();
vector< vector<int> >dp(m,vector<int>(m,INT_MAX));
dp[0][0]=triangle[0][0];
for(int i=1;i<m;i++){
dp[i][0]=dp[i-1][0]+triangle[i][0];//第一列只能从正上方下来
for(int j=1;j<i+1;j++){//考虑每一列
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+triangle[i][j];//无效化
}
}
int res= INT_MAX;
for(int j=0;j<m;j++){
res=min(res,dp[m-1][j]);//找到最后一行的最小值为题解
}
return res;
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/triangle/solution/san-jiao-xing-zui-xiao-lu-jing-he-by-xia-e8nh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
六,最长上升子序列
LeetCode 300. Longest Increasing Subsequence
方法一,
1.两个状态i,思考和最大子段和的相似之处
2.下标和个数差1【老问题了】
3.动态规划边界
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0){
return 0;
}
vector<int>dp(nums.size(),0);//dp[i]代表以第i个元素结尾的最长递增子序列
dp[0]=1;//边界
int LIS=1;
for(int i=1;i<dp.size();i++){
dp[i]=1;//单个元素最小长度是1
for(int j=0;j<i;j++){
if(nums[i]>nums[j]&&dp[i]<dp[j]+1){//后面的数比前面的数大,且当前状态长度比后面状态长度+1小,更新dp【i】
dp[i]=dp[j]+1;
}
}
if(LIS<dp[i]){
LIS=dp[i];
}
}
return LIS;
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-di-zeng-zi-xu-lie-by-xia-mu-la-ukwd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法二,
1.用vector实现的栈
2.二分查找优化,【index=-1】
int binary_search(vector<int> nums,int target){
int index=-1;
int begin=0;
int end=nums.size()-1;
while(index==-1){
int mid=(begin+end)/2;
if(target==nums[mid]){//如果target在栈中,直接取mid
index= mid;
}
else if(target>nums[mid]){//如果不在栈中,则找到应该插入的位置并【替换他】
if(mid==nums.size()-1||target<nums[mid+1]){
index=mid+1;//没有加1
}
begin=mid+1;
}
else if(target<nums[mid]){
if(mid==0||target>nums[mid-1]){
index =mid;
}
end=mid-1;
}
}
return index;
}
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0){
return 0;
}
vector<int> stack;
stack.push_back(nums[0]);
for(int i=1;i<nums.size();i++){
if(nums[i]>stack.back()){
stack.push_back(nums[i]);
}
else{
int pos=binary_search(stack,nums[i]);
stack[pos]=nums[i];
}
}
return stack.size();//还可以返回具体的序列信息
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-di-zeng-zi-xu-lie-by-xia-mu-la-gcvg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
七,最小路径和
1.【处理边界】,才能好递归
就看看运动的方向,往后看有跑到界外的吗
2.与【五,三角形的相似之处】
3.原始数组,最小值数组
4.处理边界
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
if(grid.size()==0){
return 0;
}
int row=grid.size();
int column=grid[0].size();
vector<vector<int> >dp(row,vector<int>(column,0));
dp[0][0]=grid[0][0];//边界
for(int i=1;i<column;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<column;j++){
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[row-1][column-1];
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/minimum-path-sum/solution/zui-xiao-lu-jing-he-by-xia-mu-lao-zhang-at6fb/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
八,地牢游戏
1.两个方向思考
2.由左上到右下:因为有两个重要程度相同的参数同时影响后续的决策
3.反向递归,用二维数组代表每个格子的状态,这个状态是什么
4.注意边界的无效值,赋为极大值使之无效化
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int n=dungeon.size();
int m=dungeon[0].size();
vector< vector<int> >dp(n+1,vector<int>(m+1, INT_MAX));//边界无效化
dp[n][m-1]=dp[n-1][m]=1;//特别地,无效化【处理一下边界,看看起点,至少剩一滴血】
for(int i=n-1;i>=0;i--){
for(int j=m-1;j>=0;j--){
int minn=min(dp[i+1][j],dp[i][j+1]);
dp[i][j]=max(minn-dungeon[i][j],1);//至少为1
}
}
return dp[0][0];
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/dungeon-game/solution/di-xia-cheng-you-xi-by-xia-mu-lao-zhang-xgcl9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。