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

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

9个月前 (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;
}





原创内容 转载请注明出处

分享给朋友:

相关文章

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

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

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

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

一、题目解读CSP-J 2019年的“公交换乘”题目(洛谷P5661)要求模拟地铁与公交交替出行的费用计算。题目核心在于地铁消费会产生优惠券,而公交可在45分钟内使用优惠券抵扣车费。需要处理n条出行记...

洛谷P4999题解析:动态规划求解数字拆分与求和问题(附代码)

洛谷P4999题解析:动态规划求解数字拆分与求和问题(附代码)

一、题目解读洛谷P4999题要求处理给定区间 [L, R] 内数字的拆分与求和问题。每个数字需拆分为其各位数字之和,并计算区间内所有数字之和的累加结果。题目需考虑大数情况,并采用取模运算(MOD=1e...

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

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

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

牛客4580题解:动态规划求解网格路径概率问题(C++代码实现)

牛客4580题解:动态规划求解网格路径概率问题(C++代码实现)

一、题目解读牛客4580题要求在一个n×m的网格中计算从起点(1,1)到终点(n,m)的概率。网格中存在障碍物(标记为坏点),路径只能向右或向下移动。到达终点时,若处于边界位置,概率转移规则不同:下边...

【2020蓝桥杯国赛C组】补给题解析:从Floyd到动态规划的高效解法

【2020蓝桥杯国赛C组】补给题解析:从Floyd到动态规划的高效解法

一、题目解读2020年蓝桥杯国赛C组“补给”题目要求:给定n个村庄坐标及最大补给距离D,需判断是否所有村庄均可从总部(村庄0)直接或间接到达,并计算访问所有村庄的最小路径(即旅行商问题TSP)。题目核...

发表评论

访客

看不清,换一张

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