⭐️前面的话⭐️
本篇文章介绍来自一道0-1背包的变式原题,展示语言java/C++。
📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2022年7月25日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《算法》,📚《算法导论》
💬参考在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
📌导航小助手📌
- ⭐️何以包邮⭐️
- 🔐题目详情
- 💡解题思路
- 🔑源代码
- 🌱总结
⭐️何以包邮⭐️
🔐题目详情
4700. 何以包邮?
新学期伊始,适逢顿顿书城有购书满 x 元包邮的活动,小 P 同学欣然前往准备买些参考书。
一番浏览后,小 P 初步筛选出 n 本书加入购物车中,其中第 i 本(1≤i≤n)的价格为 ai 元。
考虑到预算有限,在最终付款前小 P 决定再从购物车中删去几本书(也可以不删),使得剩余图书的价格总和 m 在满足包邮条件(m≥x)的前提下最小。
试帮助小 P 计算,最终选购哪些书可以在凑够 x 元包邮的前提下花费最小?
输入格式
输入的第一行包含空格分隔的两个正整数 n 和 x,分别表示购物车中图书数量和包邮条件。
接下来输入 n 行,其中第 i 行(1≤i≤n)仅包含一个正整数 ai,表示购物车中第 i 本书的价格。
输入数据保证 n 本书的价格总和不小于 x。
输出格式
仅输出一个正整数,表示在满足包邮条件下的最小花费。
数据范围
70% 的测试数据满足:n≤15;
全部的测试数据满足:n≤30,每本书的价格
a
i
≤
1
0
4
a_i≤10^4
ai≤104 且 x≤a1+a2+⋯+an。
输入样例1:
4 100
20
90
60
60
输出样例1:
110
样例1解释
购买前两本书 (20+90) 即可包邮且花费最小。
输入样例2:
3 30
15
40
30
输出样例2:
30
样例2解释
仅购买第三本书恰好可以满足包邮条件。
输入样例3:
2 90
50
50
输出样例3:
100
样例3解释
必须全部购买才能包邮。
💡解题思路
0-1背包问题
思路1:典型的0-1背包是求不超过某容量的最大价值,该题需要我们求超过某数的最小值,我们可以使用逆向思维,我们不直接入手,要求不超过
x
x
x的最小总价格,我们先可以将所有的商品全部选好,得到总价格
s
u
m
sum
sum,然后再从选好的商品中选出若干件,但是需要控制剩下来的总价值不小于
x
x
x,所以我们可以从这些商品中选择若干件,要想总价格不低于
x
x
x,最多只能选择
m
=
s
u
m
−
x
m=sum-x
m=sum−x价值的商品移除,因此问题就转化为:在
n
n
n件商品中选择若干件,不超过
m
m
m的最大总价格。,此时问题就转化为经典的0-1背包问题了。
- 状态定义:定义 f [ i ] [ j ] f[i][j] f[i][j]表示从前 i i i件物品中选,总价值不超过 j j j的最大总价值。
- 状态属性:最大值
- 初始状态:从 0 0 0件商品中选,最大价格为 0 0 0,因此 f [ 0 ] [ j ] = 0 f[0][j]=0 f[0][j]=0。
- 状态转移情况:考虑是否选择第
i
i
i件商品,不妨设第
i
i
i件商品价格为
t
t
t。
- 不选择第 i i i件商品, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i - 1][j] f[i][j]=f[i−1][j]。
- 选择第 i i i件商品, f [ i ] [ j ] = f [ i − 1 ] [ j − t ] + t f[i][j]=f[i-1][j-t]+t f[i][j]=f[i−1][j−t]+t。
- 状态转移方程: f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − t ] + t ) f[i][j]=max(f[i-1][j], f[i-1][j-t]+t) f[i][j]=max(f[i−1][j],f[i−1][j−t]+t)。
- 一维优化后状态转移方程: f [ j ] = m a x ( f [ j ] , f [ j − t ] + t ) f[j]=max(f[j],f[j-t]+t) f[j]=max(f[j],f[j−t]+t)
思路2:直接求。
- 状态定义:不妨定义 f [ i ] [ j ] f[i][j] f[i][j]表示在前 i i i件物品中选择,超过 j j j的最小价格。
- 状态属性:最小值
- 初始化: f [ 0 ] [ 0 ] = 0 , f [ 0 ] [ j ] = I N F , j > 0 f[0][0]=0, f[0][j]=INF,j>0 f[0][0]=0,f[0][j]=INF,j>0。
- 状态转移情况,对于第
i
i
i个商品,我们可以选择选或者不选,不妨设第
i
i
i件商品的价格为
t
t
t。
- 不选择, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i−1][j]。
- 选择,当前状态为前一行 j − t j-t j−t状态时的价格加上 t t t,如果 j < t j<t j<t,取 0 0 0状态的价格加上 t t t,即 f [ i ] [ j ] = f [ i − 1 ] [ m a x ( 0 , j − t ) ] + t f[i][j]=f[i-1][max(0,j-t)]+t f[i][j]=f[i−1][max(0,j−t)]+t。
- 状态转移方程: f [ i ] [ j ] = m i n ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ m a x ( 0 , j − t ) ] + t ) f[i][j]=min(f[i-1][j], f[i-1][max(0, j-t)]+t) f[i][j]=min(f[i−1][j],f[i−1][max(0,j−t)]+t)
- 一维优化转移方程: f [ j ] = m i n ( f [ j ] , f [ m a x ( 0 , j − t ) ] + t ) f[j]=min(f[j], f[max(0, j-t)]+t) f[j]=min(f[j],f[max(0,j−t)]+t)
🔑源代码
思路1,Java代码:
import java.util.*;
class Main {
private static final int N = (int) 33, M = (int) 3e5 +23;
private static int[] a = new int[N], f = new int[M];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int x = sc.nextInt();
int sum = 0;
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
sum += a[i];
}
int m = sum - x;
Arrays.fill(f, 0);
for (int i = 1; i <= n; i++) {
int t = a[i];
for (int j = m; j >= t; j--) {
f[j] = Math.max(f[j], f[j - t] + t);
}
}
System.out.println(sum - f[m]);
}
}
思路1,C++代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N =33, M = (int) 3e5 + 23;
int a[N], f[M];
int main()
{
int n, x;
int sum = 0;
cin >> n >> x;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i];
}
int m = sum - x;
memset(f, 0, sizeof(f));
//0-1背包
for (int i = 1; i <= n; i++)
{
int t = a[i];
for (int j = m; j >= t; j--)
{
f[j] = max(f[j], f[j - t] + t);
}
}
cout << sum - f[m] << endl;
return 0;
}
思路2,Java代码:
import java.util.*;
class Main {
private static final int N = 33, M = (int) 3e5 + 33;
private static int[] a = new int[N], f = new int[M];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int x = sc.nextInt();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
}
Arrays.fill(f, 0x3f3f3f3f);
f[0] = 0;
for (int i = 1; i <= n; i++) {
int t = a[i];
for (int j = x; j >= 0; j--) {
f[j] = Math.min(f[j], f[Math.max(j - t, 0)] + t);
}
}
System.out.println(f[x]);
}
}
思路2,C++代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 33, M = (int) 3e5 + 233;
int a[N], f[M];
int main()
{
int n, x;
cin >> n >> x;
for (int i = 1; i <= n; i++) cin >> a[i];
memset(f, 0x3f, sizeof(f));
f[0] = 0;
for (int i = 1; i <= n; i++)
{
int t = a[i];
for (int j = x; j >= 0; j--)
{
f[j] = min(f[j], f[max(j - t, 0)] + t);
}
}
cout << f[x] << endl;
return 0;
}
🌱总结
0-1背包变式题。