⭐️前面的话⭐️
本篇文章介绍来自一道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背包变式题。




















