动态规划

509. 斐波那契数

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

减少了空间复杂度

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

70. 爬楼梯

class Solution {
public:
    //1.dp[i] 到达第i台阶有dp[i]方法
    //2.递归公式dp[i]=dp[i-1]+dp[i-2]
    //3.初始化 dp[0]=0,dp[1]=1;dp[2]=2;
    //4.确定遍历顺序,从前向后
    //5.举例推导
    int climbStairs(int n) {
        if(n<=2)return n;
        vector<int>dp(n+1);
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};

746. 使用最小花费爬楼梯

class Solution {
public:
    //1.dp[i] 到达i层楼梯的最小花费dp[i]
    //2.递推公式 dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
    //3.确定初始化 dp[0]=0,dp[1]=0
    //4.确定遍历顺序  从前向后
    int minCostClimbingStairs(vector<int>& cost) {
        vector<int>dp(cost.size()+1);
        dp[0]=0;
        dp[1]=0;
        for(int i=2;i<=cost.size();i++){
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[cost.size()];
    }
};

62. 不同路径

class Solution {
public:
    //1.dp[i][j] 到达第i行第j列有dp[i][j]条路径
    //2.确定递推公式 dp[i][j]=dp[i][j-1]+dp[i-1][j]
    //确定初始化 dp[0][0]=1,dp[0][1-n-1]=1,dp[1-m-1][0]=1
    //确定遍历顺序  从上向下,从左往右

    int uniquePaths(int m, int n) {
        vector<vector<int>>dp(m,vector<int>(n,0));
        for(int i=0;i<m;i++){
            dp[i][0]=1;
        }
        for(int i=1;i<n;i++){
            dp[0][i]=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];
    }
};

63. 不同路径 II

class Solution {
public:
    //dp[i][j] 从(0,0)到(i,j)的路径方法dp[i][j]
    //确定递推函数 ob==0时 dp[i][j]=dp[i-1][j]+dp[i][j-1]
    //确定初始化 dp[0][0~n-1]=1,dp[0~m-1][0]=1  前提是ob==0,并且碰到ob==1,右边和下边dp都为0
    //递归顺序 
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m=obstacleGrid.size();
        int n=obstacleGrid[0].size();
        vector<vector<int>>dp(m,vector<int>(n,0));
        for(int i=0;i<m&&obstacleGrid[i][0]==0;i++){
            dp[i][0]=1;
        }
        for(int i=0;i<n&&obstacleGrid[0][i]==0;i++){
            if(obstacleGrid[0][i]==0)
            dp[0][i]=1;
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                if(obstacleGrid[i][j]==0){
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
                else dp[i][j]=0;
            }
        }
        return dp[m-1][n-1];
    }
};

343. 整数拆分

class Solution {
public:
    //dp[i] 分拆数字i可以得到的最大乘积dp[i]
    //递推公式 dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]))
    //确定初始化 dp[2]=1;
    int integerBreak(int n) {
        vector<int>dp(n+1);//因为数组下标从0开始,vector初始值默认为0
        dp[2]=1;
        for(int i=3;i<=n;i++){
            for(int j=1;j<=i/2;j++){
                dp[i]=max(dp[i],max(j*(i-j),j*dp[i-j]));
            }
        }
        return dp[n];
    }
};

96. 不同的二叉搜索树

class Solution {
public:
    int numTrees(int n) {
        //dp[i] 1到i为节点 组成dp[i]种二叉搜索树
        //递推公式 dp[i]+=dp[j-1]*dp[i-j]
        //dp[i]+=dp[以j为头结点左子树节点数量]*dp[以j为头结点右子树节点数量]
        //初始化 dp[0]=1
        //遍历顺序 i从小到大遍历,并用j从1遍历到i
        vector<int>dp (n+1);
        dp[0]=1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                dp[i]+=dp[j-1]*dp[i-j];
            }
        }
        return dp[n];
    }
};

46. 携带研究材料(第六期模拟笔试)(01背包模板题)

#include<bits/stdc++.h>
using namespace std;
int main(){
    //dp[i][j] 从1到i件物品中选,使容量为j的背包有最大价值dp[i][j]
    //dp[i][j]=max(dp[i-1][j],dp[i-1][j-space[i]]+value[i])
    //或者dp[i][j]=dp[i-1][j]
    //二维数组全部初始化为0,j=0全部初始化为0,
    //i=0时,初始化为0或value[0]
    //先遍历物品种类再遍历容量
    int n,m;
    cin>>m>>n;
    vector<int> space(m,0);
    vector<int>value(m,0);
    for(int i=0;i<m;i++){
        cin>>space[i];
    }
    for(int i=0;i<m;i++){
        cin>>value[i];
    }
    
    vector<vector<int>>dp(m,vector<int>(n+1,0));
    for(int j=1;j<n+1;j++){
        if(space[0]<=j) dp[0][j]=value[0];
    }
    
    for(int i=1;i<m;i++){
        for(int j=0;j<n+1;j++){
            if(j<space[i])dp[i][j]=dp[i-1][j];
            else dp[i][j]=max(dp[i-1][j],dp[i-1][j-space[i]]+value[i]);
        }
    }
    cout<<dp[m-1][n]<<endl;
    return 0;
    
}

方法2:使用滚动数组

#include<bits/stdc++.h>
using namespace std;
int main(){
    //dp[j],使容量为j的背包有最大价值dp[j]
    //dp[j]=max(dp[j],dp[j-space[i]]+value[i])
    //一维数组全部初始化为0,
    //先遍历物品种类再遍历容量,容量从大到小遍历
    int n,m;
    cin>>m>>n;
    vector<int> space(m,0);
    vector<int>value(m,0);
    for(int i=0;i<m;i++){
        cin>>space[i];
    }
    for(int i=0;i<m;i++){
        cin>>value[i];
    }
    
    vector<int>dp(n+1,0);
    
    for(int i=0;i<m;i++){
        for(int j=n;j>=space[i];j--){
             dp[j]=max(dp[j],dp[j-space[i]]+value[i]);
        }
    }
    cout<<dp[n]<<endl;
    return 0;
    
}

416. 分割等和子集

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        //dp[j] 容量为j的背包,所装物品的最大价值为dp[j]
        //dp[j]=max(dp[j],dp[j-nums[i]]+nums[i])
        //全部初始化为0
        //遍历顺序 先遍历种类,在遍历容量,容量从大到小遍历
        int sum=0;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
        }

        if(sum%2==1)return false;
        int target=sum/2;

        vector<int>dp(10001,0);
        for(int i=0;i<nums.size();i++){
            for(int j=10000;j>=nums[i];j--){
                dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }

        if(dp[target]==target) return true;
        return false;
    }
};

1049. 最后一块石头的重量 II

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        //dp[j] 容量为j的背包最多能装重量为dp[j]
        //dp[j]=max(dp[j],dp[j-nums[i]]+nums[i])
        //初始化  全为0
        //遍历顺序 先种类再容量 容量倒序
        vector<int>dp(1501,0);

        int sum=0;
        for(int i=0;i<stones.size();i++){
            sum+=stones[i];
        }

        int target=sum/2;

        for(int i=0;i<stones.size();i++){
            for(int j=target;j>=stones[i];j--){
                dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);
            }
        }

        int res=(sum-dp[target])-dp[target];
        return res;
    }
};

494. 目标和

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        //dp[i][j] 从[0~i]物品中选择能够填满容量为j的方法有dp[i][j]种方法
        //dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]] 或者dp[i][j]=dp[i-1][j]
        //初始化j=0,i=0 dp为1;i=0,当nums[i]==j时,初始化为1,其余为0
        //从左到右,从上到下遍历
        int sum=0;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
        }
        
        if((sum+target)%2==1)return 0;
        if(abs(target)>sum) return 0;

        int left=(sum+target)/2;

        //初始化dp
        vector<vector<int>>dp(nums.size(),vector<int>(left+1,0));
        for(int i=1;i<dp[0].size();i++){
            if(i==nums[0])dp[0][i]=1;
        }
        dp[0][0]=1;

        //对num值为0的初始化需要特别关注
        int numzero=0;
        for(int i=0;i<dp.size();i++){
            if(nums[i]==0) numzero++;
            dp[i][0]=(int)pow(2.0,numzero);
        }

        //遍历推导
        for(int i=1;i<dp.size();i++){
            for(int j=0;j<dp[0].size();j++){
                if(j<nums[i]) dp[i][j]=dp[i-1][j];
                else dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]];
            }
        }

        return dp[nums.size()-1][left];

    }
};

滚动数组

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        //dp[j] 填满容量为j的书包,有dp[j]种方法
        //dp[j]=dp[j-nums[i]]+dp[j] 或者dp[j]=dp[j]
        //初始化 dp[0]=1; 若j==nums[0] dp[j]=1
        //遍历顺序 先种类再容量 容量倒序

        int sum=0;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
        }
        if((sum+target)%2==1)return 0;
        if(abs(target)>sum) return 0;

        int left=(sum+target)/2;
        
        //初始化
        vector<int>dp(left+1,0);
        dp[0]=1;
        //if(nums[0]<=left) dp[nums[0]]=1;
        //因为在遍历递推过程中会给j==nums[i]的位置赋值上1,所以此时不需要像二维一样初始化

        for(int i=0;i<nums.size();i++){
            for(int j=left;j>=nums[i];j--){
                dp[j]=dp[j-nums[i]]+dp[j];
            }
        }
        return dp[left];
    }
};

474. 一和零

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        //dp[i][j] 最多i个0,j个1的子集的最大长度为dp[i][j]
        //递推公式 dp[i][j]=max(dp[i][j],dp[i-zeronum][j-onenum]+1);
        //全部初始化为0
        //先遍历物品种类,及strs的元素,再倒序遍历容量
        vector<vector<int>>dp(m+1,vector<int>(n+1,0));
        for(string str:strs){
            int zeronum=0;
            int onenum=0;
            for(char c:str){
                if(c=='0')zeronum++;
                else onenum++;
            }
            for(int i=m;i>=zeronum;i--){
                for(int j=n;j>=onenum;j--){
                    dp[i][j]=max(dp[i][j],dp[i-zeronum][j-onenum]+1);
                }
            }
        }
        return dp[m][n];
    }
};

52. 携带研究材料(第七期模拟笔试)

#include<iostream>
#include<vector>
using namespace std;
int main(){
    int n,v;
    vector<int>weight;
    vector<int>value;
    cin>>n>>v;
    for(int i=0;i<n;i++){
        int m,n;
        cin>>m>>n;
        weight.push_back(m);
        value.push_back(n);
    }
    
    vector<int>dp(v+1,0);
    
    for(int i=0;i<n;i++){
        for(int j=weight[i];j<=v;j++){
            dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }
    cout<<dp[v]<<endl;
    return 0;
}

518. 零钱兑换 II

class Solution {
public:
    //dp[j] 总金额为j的硬币组合方式
    //dp[j]=dp[j]+dp[j-conins[i]]
    int change(int amount, vector<int>& coins) {
        vector<int>dp(amount+1,0);
        dp[0]=1;
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<=amount;j++){
                dp[j]+=dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
};

377. 组合总和 Ⅳ

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        //dp[j] 装满容量为j的背包有多少种方法
        //dp[j]+=dp[j-nums[i]]
        //dp[0]=1,其他都为0
        //先遍历容量,再遍历种类
        vector<int> dp(target+1,0);
        dp[0]=1;
        for(int j=0;j<=target;j++){
            for(int  i=0;i<nums.size();i++){
                if(j>=nums[i]&&dp[j]<INT_MAX-dp[j-nums[i]])//测试用例中有相加超过int的数据
                dp[j]+=dp[j-nums[i]];
            }
        }
        return dp[target];
    } 
};

思考:01背包 对容量的遍历,如果是滚动数组 ,要倒序遍历!!因为每个物品只有一个

完全背包 则不需要倒序遍历,因为每个物品都有无数个

57. 爬楼梯(第八期模拟笔试)

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

int main(){
    //dp[j] 到达第j阶楼梯有dp[j]种方法
    //dp[j]+=dp[j-i];
    //dp[0]=1;其余初始化成0
    //先容量后种类
    int n,m;
    cin>>n>>m;
    vector<int>dp(n+1,0);
    dp[0]=1;
    for(int j=0;j<=n;j++){
        for(int i=1;i<=m;i++){
            if(j>=i){
                dp[j]+=dp[j-i];
            }
        }
    }
    cout<<dp[n]<<endl;
    return 0;
}

322. 零钱兑换

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        //dp[j] 组成j所需的最少的硬币个数
        //dp[j]=min(dp[j],dp[j-coins[i]]+1);
        //dp[0]=0,其余初始化成最大值
        //先遍历种类,再遍历容量
        vector<int>dp(amount+1,INT_MAX);
        dp[0]=0;
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<=amount;j++){
                if(dp[j-coins[i]]!=INT_MAX)
                dp[j]=min(dp[j],dp[j-coins[i]]+1);
            }
        }
        if(dp[amount]!=INT_MAX) return dp[amount];
        else return -1;
    }
};

279. 完全平方数

class Solution {
public:
    int numSquares(int n) {
        //dp[j] 和为j的完全平方数的最少数量为dp[j]
        //dp[j]=min(dp[j],dp[j-i])
        //dp[0]=0;其余初始化成最大值
        //先后不影响  先容量再种类 因为种类不确定
        vector<int>dp(n+1,INT_MAX);
        dp[0]=0;
        for(int j=0;j<=n;j++){
            for(int i=0;i*i<=j;i++){
                if(dp[j-i*i]!=INT_MAX)//因为我的种类是从0开始遍历的,会碰到dp[j-i*i]=dp[j],也就是此时是dp[j]第一次被赋值,其原始数据是INT_MAX。其实可以直接跳过i=0,遍历i=0,时,dp数组没有任何改变
                dp[j]=min(dp[j],dp[j-i*i]+1);
            }
        }
        return dp[n];

    }
};

到此为止,学习了01背包和完全背包

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值