【背包问题】完全背包

目录

前言:

一,完全背包问题

问题描述:

模板题目:

 题目解析:

代码: 

空间优化:

二,典例

1,零钱兑换

 题目解析:

算法分析:

代码:

空间优化:

2,零钱兑换2

题目解析:

算法分析:

代码:

空间优化:

 3,完全平方数

题目解析:

算法解析:

代码: 

空间优化:


前言:

上篇:01背包优快云

一,完全背包问题

问题描述:

        有n个物品,和一个容量为V的背包,每种物品都可以无限使用。每个物品都有两个属性,体积v和价值w。求解:将那些物品放入背包,可使这些物品的总体积不超过背包的容量,且总价值最大,输出最大价值。

        与01背包问题不同的是,完全背包的物品是可以无限选取的,而01背包的物品只会面临选或者不选。

模板题目:

题目链接:【模板】完全背包_牛客题霸_牛客网

 题目解析:

        与01背包问题相似,我们定义出状态表示:dp[i][j]表示,在前i个物品中选,总体积不超过j情况下,所能装的最大价值。

 

 

        对于第二问,状态表示为: dp[i][j]表示,在前i个物品中选,总体积正好等于j的情况下,所能装的最大价值。

代码: 

#include <iostream>
using namespace std;
#include <string.h>


const int N=1010;
int n,V,v[N],w[N];
int  dp[N][N];
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }

    for(int i=1;i<=n;i++)
    for(int j=0;j<=V;j++)
    {
        dp[i][j]=dp[i-1][j];
        if(j>=v[i])
        dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
    }
    cout<<dp[n][V]<<endl;

    memset(dp,0,sizeof(dp));
    for(int j=1;j<=V;j++)
    dp[0][j]=-1;

    for(int i=1;i<=n;i++)
    for(int j=0;j<=V;j++)
    {
        dp[i][j]=dp[i-1][j];
        if(j>=v[i]&&dp[i][j-v[i]]!=-1)
        dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
    }
    cout<<(dp[n][V]==-1?0:dp[n][V])<<endl;
    return 0;
}

空间优化:

        利用滚动数组做空间上的优化,与01背包的原理相似

#include <iostream>
using namespace std;
#include <string.h>


const int N=1010;
int n,V,v[N],w[N];
int  dp[N];
int main()
{
    cin>>n>>V;
    for(int i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i];
    }

    for(int i=1;i<=n;i++)
    for(int j=v[i];j<=V;j++)
    {
        dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    }
    cout<<dp[V]<<endl;

    memset(dp,0,sizeof(dp));
    for(int j=1;j<=V;j++)
    dp[j]=-1;

    for(int i=1;i<=n;i++)
    for(int j=v[i];j<=V;j++)
    {
        if(dp[j-v[i]]!=-1)
        dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    }
    cout<<(dp[V]==-1?0:dp[V])<<endl;
    return 0;
}

二,典例

1,零钱兑换

本题链接:LCR 103. 零钱兑换 - 力扣(LeetCode)

 

 题目解析:

        coins数组中不同面值的硬币,可以看成是n个物品。从coins数组中挑选硬币,使其总和等于amount的最少硬币数。其中,每种硬币是可以无线选取的。可以用完全背包的思路解题。

算法分析:

        

代码:
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int n=coins.size();
        const int INF=0x3f3f3f3f;

        vector<vector<int>> dp(n+1,vector<int>(amount+1));
        for(int j=1;j<=amount;j++)
        dp[0][j]=INF;

        for(int i=1;i<=n;i++)
        for(int j=0;j<=amount;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(j>=coins[i-1])
            dp[i][j]=min(dp[i][j],dp[i][j-coins[i-1]]+1);
        }

        return dp[n][amount]>=INF?-1:dp[n][amount];
    }
};
空间优化:
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int n=coins.size();
        const int INF=0x3f3f3f3f;

        vector<int> dp(amount+1);
        for(int j=1;j<=amount;j++)
        dp[j]=INF;

        for(int i=1;i<=n;i++)
        for(int j=coins[i-1];j<=amount;j++)
        {
            dp[j]=min(dp[j],dp[j-coins[i-1]]+1);
        }

        return dp[amount]>=INF?-1:dp[amount];
    }
};

2,零钱兑换2

本题链接:518. 零钱兑换 II - 力扣(LeetCode)

题目解析:

        本题求的是不同的组合数,上题是求最少硬币数。

算法分析:

 

代码:
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int n = coins.size();

        //数据相加后 可能会超出范围
        vector<vector<double>> dp(n + 1, vector<double>(amount + 1, 0));
        dp[0][0] = 1;

        for (int i = 1; i <= n; i++)
            for (int j = 0; j <= amount; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= coins[i - 1])
                    dp[i][j] += dp[i][j - coins[i - 1]];
            }

        return dp[n][amount];
    }
};
空间优化:
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int n = coins.size();

        vector<double> dp(amount + 1);
        dp[0] = 1;

        for (int i = 0; i < n; i++)
            for (int j = coins[i]; j <= amount; j++)
                dp[j] += dp[j - coins[i]];

        return dp[amount];
    }
};

 3,完全平方数

本题链接:279. 完全平方数 - 力扣(LeetCode)

题目解析:

         一个整数n拆成几个完全平方数的和,求完全平方数的最少数量。

算法解析:

        对于一个整数n,我们在找它的完全平方数和时,只会在1到根号n之前找。比如中13的完全平方数,我们会找4和9,即2的平方和3的平方。

代码: 
class Solution {
public:
    int numSquares(int n) {
        int m=sqrt(n);

        vector<vector<int>> dp(m+1,vector<int>(n+1));
        for(int j=1;j<=n;j++)
        dp[0][j]=0x3f3f3f3f;

        for(int i=1;i<=m;i++)
        for(int j=0;j<=n;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(j>=i*i)
            dp[i][j]=min(dp[i][j],dp[i][j-i*i]+1);
        }
        return dp[m][n];
    }
};
空间优化:
class Solution {
public:
    int numSquares(int n) {
        int m=sqrt(n);

        vector<int> dp(n+1);
        for(int j=1;j<=n;j++)
        dp[j]=0x3f3f3f3f;

        for(int i=1;i<=m;i++)
        for(int j=i*i;j<=n;j++)
        {
            dp[j]=min(dp[j],dp[j-i*i]+1);
        }
        return dp[n];
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值