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];
}
};
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];
}
};
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()];
}
};
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];
}
};
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];
}
};
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];
}
};
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;
}
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;
}
};
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;
}
};
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];
}
};
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;
}
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];
}
};
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;
}
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;
}
};
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背包和完全背包