当前位置:首页 > 洛谷 > 从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

4个月前 (05-24)

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化 洛谷 动态规划 滚动数组  01背包 状态转移方程 第1张

题目重解:

小A带着m元钱来到餐馆,菜单上有n道菜,每道菜都有确定的价格。现在需要计算出刚好花完m元的点菜方案总数。这个问题看似简单,但当菜品数量增多时,暴力枚举就会变得不可行,需要更高效的算法来解决。

解题思路:

动态规划方法解决这个问题。首先定义一个二维数组dp,其中dp[i][j]表示前i道菜花费j元的方案数。初始化时,dp[0][0]为1(0道菜花0元有1种方案)。然后通过双重循环遍历每道菜和每个可能的金额:

1.当当前菜品价格大于当前金额时,直接继承前i-1道菜的结果

2.当金额正好等于菜品价格时,方案数增加1(单独点这道菜)

3.当金额大于菜品价格时,方案数为不选这道菜的方案数加上选了这道菜后剩余金额的方案数之和 最终dp[n][m]就存储了恰好花完m元的方案总数。

代码+注释:

#include<iostream>
using namespace std;

int main()
{
    int n,m;  // n是菜品数量,m是总金额
    cin>>n>>m;
    int* a=new int[n];  // 存储每道菜的价格
    for(int i=0;i<n;i++)
        cin>>a[i];
    
    // 初始化动态规划数组
    int** dp=new int*[n+1];
    for(int i=0;i<=n;i++){
        dp[i]=new int[m+1];
        for(int j=0;j<=m;j++) dp[i][j]=0; // 初始化为0
    }
    dp[0][0]=1;  // 基础情况:0道菜花0元有1种方案

    // 动态规划过程
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(j<a[i-1]) dp[i][j]=dp[i-1][j];  // 当前菜太贵,无法选择
            else if(j==a[i-1]) dp[i][j]=dp[i-1][j]+1;  // 刚好可以单独点这道菜
            else dp[i][j]=dp[i-1][j]+dp[i-1][j-a[i-1]];  // 不选或选这道菜两种情况之和
        }
    }
    cout<<dp[n][m];  // 输出最终结果

    // 释放内存
    for(int i=0;i<=n;i++) delete[] dp[i];
    delete[] dp;
    delete[] a;
    return 0;
}

 空间优化:

通过滚动数组技术将空间复杂度从O(nm)降低到O(m)。核心思想是发现每次状态转移只依赖于前一轮的状态,因此只需要维护两行数组即可。

#include<iostream>
using namespace std;

int main()
{
    int n,m;  // n菜品数,m总金额
    cin>>n>>m;
    int a;  // 当前菜品价格
    
    // 只申请两行的滚动数组
    int** dp=new int*[2];
    for(int i=0;i<2;i++){
        dp[i]=new int[m+1]{0};  // 初始化为0
    }
    dp[0][0] = 1;  // 基础状态
    
    bool now=0;  // 当前行标识(0/1)
    for(int i=1;i<=n;i++){
        cin>>a;
        for(int j=1;j<=m;j++){
            if(j<a) dp[now][j]=dp[now^1][j];
            else if(j==a) dp[now][j]=dp[now^1][j]+1;
            else dp[now][j]=dp[now^1][j]+dp[now^1][j-a];
        }
        now^=1;  // 切换当前行
    }
    cout<<dp[now^1][m];  // 输出最终结果
    
    // 释放内存
    for(int i=0;i<2;i++) delete[] dp[i];
    delete[] dp;
    return 0;
}





原创内容 转载请注明出处

分享给朋友:

相关文章

2024年GESP五级武器强化(洛谷B4071)解题代码C++版

2024年GESP五级武器强化(洛谷B4071)解题代码C++版

一、题目解读    2024年GESP(青少年软件编程能力等级考试)五级中的“武器强化”(洛谷平台题目编号B4071)是一道典型的算法优化问题。题目要求通过合理...

NOIP 2008火柴棒等式题解(C++代码实现)  动态规划与枚举算法详解

NOIP 2008火柴棒等式题解(C++代码实现) 动态规划与枚举算法详解

一、题目解读火柴棒等式问题(NOIP 2008,洛谷P1149)要求使用给定数量的火柴棒,构造形如 A + B = C 的等式,其中A、B、C均为整数,且火柴棒总数恰好等于输入值。需统计符合条件的等式...

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

一、题目解读LeetCode 120题“三角形最小路径和”要求给定一个由数字组成的三角形,从顶部开始向下移动,每次可向左或向右移动一格,计算从顶至底的最小路径和。三角形以二维向量形式给出,每层元素数量...

力扣931题最小下降路径和解析 动态规划解法 LeetCode解题技巧

力扣931题最小下降路径和解析 动态规划解法 LeetCode解题技巧

一、题目解读力扣931题「Minimum Falling Path Sum」(最小下降路径和)要求在一个n x n的整数矩阵中,计算从顶部到底部的最小路径和。路径只能从每个位置向下或对角线移动(即向下...

2022 CSP-J 上升点序(洛谷P8816)解题报告:动态规划求解最长上升序列

2022 CSP-J 上升点序(洛谷P8816)解题报告:动态规划求解最长上升序列

一、题目解读2022年CSP-J题目“上升点序”(洛谷P8816)要求给定一个平面点集,每个点的坐标(x,y)均为整数。题目需要构造一个最长的上升序列,序列中相邻点的坐标满足x和y均严格递增。允许使用...

【蓝桥杯国赛A组】冰山体积计算:动态规划与map统计的解题方案(洛谷P8767)

【蓝桥杯国赛A组】冰山体积计算:动态规划与map统计的解题方案(洛谷P8767)

一、题目解读本题为2021年蓝桥杯国赛A组题目“冰山”(洛谷P8767),要求处理冰山在融化与新生成过程中的体积变化。每日存在两种操作:冰山体积按固定值x融化(体积不足x的部分视为完全融化),以及新增...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。