​小A点菜 - 洛谷​

一、题目信息

1、题目链接:小A点菜 - 洛谷

二、题目分析

1、分析:

  • 求点菜方法的最大数量,那么dp矩阵的元素就是点菜方法的最大数量,两个维度分别为可点菜品的最大序号以及可用钱数。
  • 对于第1~第i道菜,和可用的j元钱,有以下几种情况:(values[i]表示第i道菜的价格)
    • j<values[i],钱不够买第i道菜,只能花在前i-1道菜上,因此dp[i][j]=dp[i-1][j]
    • j==values[i],有两种选择,将钱全花在第i道菜上(有1种可能) 和 将钱全花在前i-1道菜上(有dp[i-1][j-values[i]]种可能),因此dp[i][j]=1+dp[i-1][j-values[i]]
    • j>values[i],有两种选择,将钱一部分花在第i道菜上、一部分花在前i-1道菜上(有dp[i-1][j-values[i]]种可能) 将钱全花在前i-1道菜上(有dp[i-1][j-values[i]]种可能)
  • 而当菜品数为0或钱数为0时,点菜方法为0,因此dp[i][0]和dp[0][j](0<=i<=n,0<=j<=m)均初始化为0

2、思路:...

三、代码

#include<iostream>
#include<vector>
using namespace std;
int main(){
    int n,m;cin>>n>>m;
    int values[n+1];
    for(int i=1;i<=n;i++) cin>>values[i];
    vector<vector<int>> dp(n+1,vector<int>(m+1,0));
    for(int i=0;i<n;i++){
        dp[i][0]=0;
    }
    for(int j=0;j<m;j++){
        dp[0][j]=0;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(j>values[i]){ //分为《将钱的一部分花在第i个菜品上,剩下的花在前i-1个菜品上》 和 《将钱只花在前i-1个菜品上》两种情况
                dp[i][j]=dp[i-1][j-values[i]]+dp[i-1][j];
            }else if(j==values[i]){  //分为《将钱全花在第i个菜品上》 和 《将钱只花在前i-1个菜品上》两种情况
                dp[i][j]=1+dp[i-1][j];
            }
            else{  //钱不够买第i道菜,只能将钱花在前i-1个菜品上
                dp[i][j]=dp[i-1][j];
            }
        }
    }
    cout<<dp[n][m];
    return 0;
}

四、优化

1、时间上:

  • 将dp[0~n][0]初始化为1,那么当j==values[i]时,dp[i-1][j-values[i]]=1,可以减少判断次数
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> values(n + 1);
    for (int i = 1; i <= n; i++) cin >> values[i];

    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));

    for(int i=0;i<=n;i++){
        dp[i][0]=1;
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            dp[i][j] = dp[i - 1][j];  // 不选择第 i 个菜品
            if (j >= values[i]) {
                dp[i][j] += dp[i - 1][j - values[i]];  // 选择第 i 个菜品
            }
        }
    }

    cout << dp[n][m] << endl;

    return 0;
}

2、空间上:

  • 由于dp[i][j]只依赖于dp[i-1][0~j],因此菜品维度i可以省去。同时可以逆向遍历避免覆盖
#include<iostream>
#include<vector>
using namespace std;
int main(){
    int n,m;cin>>n>>m;
    int values[n+1];
    for(int i=1;i<=n;i++) cin>>values[i];
    vector<int> dp(m+1,0);dp[0]=1;
    for(int i=1;i<=n;i++){
        for(int j=m;j>0;j--){
            //未更新前,i时(此时)的dp[j] 等于 i-1时的dp[j],意味着《将j元钱花在前i-1个菜品上》
            if(j>=values[i]){ //加上《将钱的一部分花在第i个菜品上,剩下的花在前i-1个菜品上》的情况
                dp[j]+=dp[j-values[i]]; //这里的dp[j-values[i]]是i-1时的值
            }
        }
    }
    cout<<dp[m];  //输出第n个时间步的dp[m]
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值