一、题目信息
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;
}