商店 Problem Description
在商店里有N个物品,每个物品有原价和折扣价小美相要购买商品。小美拥有X元,一共Y张折扣券。小美需要最大化购买商品的数量,并在所购商品数量尽量多的前提下,尽量减少花费。 你的任务是帮助小美求出最优情况下的商品购买数量和花费的钱数。
input
第一行三个整数,以空格分开,分别表示N,X,Y。接下来N行,每行两个整数,以空格分开,表示一个的原价和折扣价。1≤N≤100,1≤X≤5000,1≤Y≤50,每个商品原价和折扣价均介于[1,50]之间。
ouput
一行,两个整数,以空格分开。第一个数字表示最多买几个商品,第二个数字表示在满足商品尽量多的前提下所花费的最少的钱数。
Sample Input 1
3 5 1
4 3
3 1
6 5Sample Output 1
2 5
Sample Input 2
3 5 1
4 3
3 1
6 1Sample Output 2
2 4
Sample Input 3
10 30 3
2 1
3 2
2 1
10 8
6 5
4 3
2 1
10 9
5 4
4 2Sample Output 3
8 24
题目类型、难度、来源
- 类型:动态规划
- 难度:困难
- 来源:美团校招-2023.3.18.10点-第四题-商店
总体思路:
- 此题的难点在于有不确定数量的折扣卷。如果折扣卷无限或没有折扣卷,那么这题就可以使用贪心来做。
- 此题应该用动态规划,使用** d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示前i个商品,花费了j元,用掉了k张折扣卷能买到的商品数量**。使用price[i]表示第i个商品的价格,lowprice[i]表示第i个商品的折扣价。
- i从1开始,前i=0个表示个表示没有商品,因此边界条件就确定了,即 d p [ 0 ] [ 0 ] [ 0 ] = 0 dp[0][0][0]=0 dp[0][0][0]=0。使用-1表示状态该状态非法。
- 状态转移:在考虑第i个商品时,只有三种情况:
- 不买第i个商品。此时很显然用掉的钱及用掉的折扣卷数量和前i-1个商品的情况一样,即 d p [ i ] [ j ] [ k ] = d [ i − 1 ] [ j ] [ k ] dp[i][j][k] = d[i-1][j][k] dp[i][j][k]=d[i−1][j][k]
- 原价购买第i个商品。此时有 d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j − p r i c e [ i ] ] [ k ] + 1 dp[i][j][k] = dp[i-1][j-price[i]][k]+1 dp[i][j][k]=dp[i−1][j−price[i]][k]+1
- 在有折扣卷的情况下,使用折扣卷购买第i个商品。此时有 d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j − l o w p r i c e [ i ] ] [ k − 1 ] + 1 dp[i][j][k] = dp[i-1][j-lowprice[i]][k-1]+1 dp[i][j][k]=dp[i−1][j−lowprice[i]][k−1]+1
- 因为题目要求最多买多少商品,因此状态转移方程为: d p [ i ] [ j ] [ k ] = m a x ( d [ i − 1 ] [ j ] [ k ] , d p [ i − 1 ] [ j − p r i c e [ i ] ] [ k ] + 1 , d p [ i − 1 ] [ j − l o w p r i c e [ i ] ] [ k − 1 ] + 1 ) dp[i][j][k]=max(d[i-1][j][k], dp[i-1][j-price[i]][k]+1, dp[i-1][j-lowprice[i]][k-1]+1) dp[i][j][k]=max(d[i−1][j][k],dp[i−1][j−price[i]][k]+1,dp[i−1][j−lowprice[i]][k−1]+1)
- 因为状态转移方程过于复杂,很难找到一个更新dp数组的顺序。因此需要反过来思考。即使用 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]去更新 d [ i + 1 ] [ j ] [ k ] d[i+1][j][k] d[i+1][j][k]、 d p [ i + 1 ] [ j + p r i c e [ i + 1 ] ] [ k ] dp[i+1][j+price[i+1]][k] dp[i+1][j+price[i+1]][k]、 d p [ i + 1 ] [ j + l o w p r i c e [ i + 1 ] ] [ k + 1 ] dp[i+1][j+lowprice[i+1]][k+1] dp[i+1][j+lowprice[i+1]][k+1]
AC代码
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
int N, X, Y;
cin >> N >> X >> Y;
// 原价
int *price = new int[N+5];
//折扣价
int *low_price = new int[N+5];
for (int i = 0; i < N; i++){
cin >> price[i] >> low_price[i];
}
// 初始化dp数组
int dp[N+5][X+10][Y+10] = {
0};
for (int i = 0; i <= N; i++){
for (int j = 0; j <= X; j++){
for (int k = 0; k <= Y; k++){
dp[i][j][k] = -1;
}
}
}
// 前i=0个商品表示没有商品,因此dp[0][0][0]只能为0
dp[0][0][0] = 0;
for (int i = 0; i < N; i++){
for (int j = 0; j <= X; j++){
for (int k = 0; k <= Y; k++){
if (dp[i][j][k] == -1) continue;
dp[i+1][j][k] = max(dp[i][j][k], dp[i+1][j][k]);
if (j+price[i] <= X){
dp[i+1][j+price[i]][k] = max(dp[i][j][k]+1, dp[i+1][j+price[i]][k]);
}
if (j+low_price[i] <= X && k+1 <= Y){
dp[i+1][j+low_price[i]][k+1] = max(dp[i][j][k]+1, dp[i+1][j+low_price[i]][k+1]);
}
}
}
}
int max_sum = -1, min_cost = 1000000000;
for (int i = 1; i <= N; i++){
for (int j = 0; j <= X; j++){
for (int k = 0; k <= Y; k++){
if (dp[i][j][k] > max_sum){
max_sum = dp[i][j][k];
min_cost = j;
}else if (dp[i][j][k] == max_sum){
if (j < min_cost){
min_cost = j;
}
}
}
}
}
cout << max_sum << " " << min_cost;
return 0;
}
- 更多大厂真题可以看:2023实习、秋招互联网大厂技术岗算法真题-刷题(持续更新)