202209-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
Data

样例1输出

110
Data

样例1解释

购买前两本书(20+90)即可包邮且花费最小。

样例2输入

3 30
15
40
30
Data

样例2输出

30
Data

样例2解释

仅购买第三本书恰好可以满足包邮条件。

样例3输入

2 90
50
50
Data

样例3输出

100
Data

样例3解释

必须全部购买才能包邮。

子任务

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

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

提示

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

python

 

#01背包
'''
        0-1背包:选择的物品总体积<=背包容量,求价值最大
                        dp[j]:背包容量为j时,可以装的物品最大价值

        此题:    选择的书总价值>=邮费x,求价值最小
                        dp[j]:邮费限制为j时,可以选择的书的最多价值
'''
n,x=map(int,input().split())
a=[]
for _ in range(n):
    b=int(input())
    a.append(b)
dp=[0]*(n*x)
ss=sum(a)
for i in range(n):#
    for j in range(ss,a[i]-1,-1):
        dp[j]=max(dp[j],dp[j-a[i]]+a[i])
for i in range(x,ss+1):
    if(dp[i]>=x):
        print(dp[i])
        break
        '删掉的书的总价格,上限是sum-x,因为总要大于x才能包邮'
        '通过删掉不同书来改变这个状态,最后找到最接近上限的状态。'
        '''
        前i本书在j的价格上限内,可以删掉的总价格=
        max(前i-1本书在j上限可以删掉的总价格 , 前i本书在j-ai的价格上限可以删掉的总价格+ai)
        =max(不删这本书,删这本书)
        如果在上限j删这本书,那么之前就不能删这本书,所以删之前的上限是j-ai
        '''

 

 

#01背包
'''
既然不能通过枚举所有的情况来找到答案(得分70分) ,那肯定是有一种途径来找到最优解,所以自然的联想到了dp(动态规划);我们再来看一下这个问题,寻找超过包邮条件x的最小数字组合。可以转化为先求书的总价sum,再用sum减去x得y,问题就变成了寻找不超过y(达到包邮条件)的最大数字组合,再用总价减去这个数字组合,即可得到最终答案。寻找不超过y的最大数字组合,这种类型就是典型的背包问题。

比如商品总价格:sum=20+90+60+60=230   x= 100(满100包邮)    y=230-100=130 寻找在130范围内的最大商品.当然我们从开始的思路就是逆向来的,所以最后回溯算法得到的列表也是不包含题目答案的答案.
————————————————
maxValue[商品][背包容量]
'''
n,x =map(int,input().split())
price=[0]+[i for i in map(int,input().split())]
pre =sum(price)    #计算总价钱
price.sort()   #可以省略
y =[i for i in range(pre-x+1)]#从0到y 也就是可以删去的价钱数目
maxValue =[ [0 for i in range(pre -x+1)] for j in range(n+1)]  #第一行为0行  第一列为0列
#一共n+1行 每行都是一个0-pre -x+1的0列表

for i in range(1,len(price)+1):#相当于物品行 遍历所有物品
    weight = price[i-1]  #重量
    value =price[i-1]  #价值
    for j in range(len(y)):#删去上限为j
        maxValue[i][j]=maxValue[i-1][j]   #0/1背包主要代码①
        #商品i 删去上限为j 时最大价值
        if j >= weight:      #如果背包重量大于=当前物品的重量 就相当于我们可以还以塞其他物品
            maxValue[i][j] = max(maxValue[i-1][j],maxValue[i-1][j-weight]+value)  #0/1背包主要代码②
            #没有加i行物品的价值 , 在加入上一行物品前提下,背包重量腾出i行物品的重量+i行物品的价值
# 输出看看我们的maxValue矩阵
#print(pre - maxValue[-1][-1])
# for i in range(n+1):
#     print(maxValue[i])
 
#回溯寻找
def huishuo(maxValue,items):
    sequence=[]
    #i行代表物品行  j列 代表weight
    i =len(maxValue)-1
    j =len(maxValue[0])-1
    while i >0 and j >0 :
        if maxValue[i][j] == maxValue[i-1][j]:  #相等代表没有加进来这个物品
            i -=1
        else:
            sequence.append(price[i-1])    #添加进来这个物品   因为price是从1开始 所以i-1
            j =j- price[i-1]
            # j 减去 这个东西的重量  (没放进去这个商品的书包的容量)
            i  -=1      #往上一行寻找
    return list(reversed(sequence))     #因为回溯寻找  最后要将结果倒置
result= huishuo(maxValue,price)
# print(result)
 
#以下结果为我们真正题目要求的结果
final =[]
for  i in range(len(price)):
    if price[i] not  in result:
        final.append(price[i])
print(sum(final))

 

posted @ 2023-03-06 15:25  吃人不吐葡萄  阅读(48)  评论(0)    收藏  举报