CSP202209-2 何以包邮?

题目描述

新学期伊始,适逢顿顿书城有购书满 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。

输出格式

输出到标准输出。

仅输出一个正整数,表示在满足包邮条件下的最小花费。

样例1输入

4 100

20

90

60

60

样例1输出

110

子任务

70% 的测试数据满足:n≤15;

全部的测试数据满足:n≤30,每本书的价格 ai≤104 且 x≤a1+a2+⋯+an。

提示

对于 70% 的测试数据,直接枚举所有可能的情况即可。

典型的0-1背包是求不超过某容量的最大价值,此题需要我们求超过某数的最小值,使用逆向思维,问题就转化为:在不超过物品总价减去包邮的价格的情况下,使得我们要带走的书籍价格最高。

二维解法:

#include <bits/stdc++.h> 
using namespace std;
int a[1000005];
int dp[50][300005];                                                                                                  
signed main()
{
    int n,x;
    cin>>n>>x;
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    int m=sum-x;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(j>=a[i])    dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+a[i]);
            else  dp[i][j]=dp[i-1][j];
        }
    cout<<sum-dp[n][m]<<endl;   
    return 0;
    }

一维解法(滚动数组):

一维dp数组(滚动数组),其实就是对背包问题的状态压缩;在使用二维数组来表示dp时,递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);与其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。 二维dp遍历顺序中背包容量是从小到大,一维dp遍历顺序背包容量是从大到小,一维dp背包容量倒序遍历的目的是保证物品i只被放入一次,若采用正序遍历会导致物品i被重复加入多次。

#include <bits/stdc++.h> 
using namespace std;
int a[1000005];
int f[1000005];                                                                                                  
signed main()
{
    int n,x;
    cin>>n>>x;
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum+=a[i]; //书的总价格
    }
    int m=sum-x;
    //滚动数组实现,二维降一维
    for(int i=1;i<=n;i++)//遍历物品
        for(int j=m;j>=a[i];j--)//从后向前遍历背包容量 避免重复
            f[j]=max(f[j],f[j-a[i]]+a[i]);
    cout<<sum-f[m]<<endl;
    return 0;
}

01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了,一维dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值