新学期伊始,适逢顿顿书城有购书满 x x x 元包邮的活动,小 P 同学欣然前往准备买些参考书。
一番浏览后,小 P 初步筛选出 n n n 本书加入购物车中,其中第 i i i 本 ( 1 ≤ i ≤ n ) (1 \leq i \leq n) (1≤i≤n) 的价格为 a i a_i ai 元。
考虑到预算有限,在最终付款前小 P 决定再从购物车中删去几本书(也可以不删),使得剩余图书的价格总和 m m m 在满足包邮条件 ( m ≤ x ) (m \leq x) (m≤x) 的前提下最小。
试帮助小 P 计算,最终选购哪些书可以在凑够 x x x 元包邮的前提下花费最小?
从标准输入读入数据。
输入的第一行包含空格分隔的两个正整数 n n n 和 x x x,分别表示购物车中图书数量和包邮条件。
接下来输入 n n n 行,其中第 i i i 行 ( 1 ≤ i ≤ n ) (1 \leq i \leq n) (1≤i≤n) 仅包含一个正整数 a i a_i ai,表示购物车中第 i i i 本书的价格。输入数据保证 n n n 本书的价格总和不小于 x x x。
输出到标准输出。
仅输出一个正整数,表示在满足包邮条件下的最小花费。
4 100
20
90
60
60
110
购买前两本书 ( 20 + 90 ) (20+90) (20+90) 即可包邮且花费最小。
3 30
15
40
30
30
仅购买第三本书恰好可以满足包邮条件。
2 90
50
50
100
必须全部购买才能包邮。
70% 的测试数据满足: n ≤ 15 n \leq 15 n≤15;
全部的测试数据满足: n ≤ 30 n \leq 30 n≤30,每本书的价格 a i ≤ 1 0 4 a_i \leq 10^4 ai≤104 且 x ≤ a 1 + a 2 + ⋅ ⋅ ⋅ + a n x \leq a_1+a_2+···+a_n x≤a1+a2+⋅⋅⋅+an。
对于 70% 的测试数据,直接枚举所有可能的情况即可。
#include
using namespace std;
int main(){
int n, x;
cin >> n >> x;
vector<int>a(31); //每本书的价格
int sum = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum += a[i];
}
int temp;
temp = sum -x; //需要选出的书籍的价格总和
int dp[31][100001]; //这是一个动态规划的问题
for(int i = 1; i <= n; i++){ //外层循环遍历每本书
for(int j = 1; j <= temp; j++){ //内层循环遍历总价值不超过temp的情况
/*
对于每个状态dp[i][j],有两种选择:选取第i本书或不选取
如果不选取第i本书,则dp[i][j] = dp[i-1][j];
如果选择第i本书,则dp[i][j] = dp[i-1][j-a[i]]+a[i],取两者之间的最大值。
*/
dp[i][j] = dp[i-1][j];
if(j >= a[i]){
dp[i][j] = max(dp[i][j], dp[i-1][j-a[i]]+a[i]);
//dp[i][j]表示在前i本书中选取总价不超过j的最大价格
}
}
}
int result = 0;
result = sum - dp[n][temp]; //计计算选出的书籍的价格总和,并将其减去所有书籍的价格总和
cout << result <<endl;
return 0;
}
本题代码属于动态规划算法的题型。具体而言,这个问题可以称为「01背包问题」的变种。在「01背包问题」中,我们有一组物品,每个物品都有自己的重量和价值,目标是选择一些物品装入背包,使得背包的总重量不超过限制,并且价值最大化。而这个问题中,我们需要选择一些书籍的价格,使得总价格不超过限制,并且尽量高。