Hrbust 1333 GG的关心【01背包】

电脑买回来后,MM一直用它专心的搞ACM,看着MM专注的样子,GG也是非常开心,但害怕MM劳累过度影响身体,于是GG经常到超市给MM买一些营养品来补充身体能量。GG到超市购物从来都是刷卡的,可是这种卡很诡异,当用它在超市购物的时候,刷卡机会先判断卡上的余额是否低于5元钱。如果低于5元钱则无法购买任何物品,即使那件物品的金额是小于5的也不可以。而如果卡上的金额大于等于5元则可以进行交易,即使交易成功后卡中余额为负数也是可以的。

现在超市有n种物品,每种物品最多只能买一个。已知每种物品的价格和卡上的余额。问最少可使卡上的余额为多少。

Input
输入包含多组数据。对于每一组数据:

第一行有两个正整数n和m,分别代表物品的种类数,以及卡中的余额。

第二行包含n个正整数p1,p2,p3…pn,分别代表每一种物品的价格。

当n为0时表示输入结束

范围:

m, n <= 1000

pi <= 50 (1 <= i <= n)

Output
对于每组输入,输出一个整数并换行,代表卡上可能的最小余额。

Sample Input
1 5
50
10 50
1 2 3 2 1 1 2 3 2 1
0
Sample Output
-45
32
Hint
买东西是一件一件买。

题解: 对于这题可以这么想,他要求余额最少,并且当余额大于等于5元时仍可以买一件任意价格的物品。于是很容易想到,一定要留个5元来买最贵的物品。
那么真正的可以金额就是 初始金额-5 。
真正可用金额花的越多,剩下的越少,在买完最贵的物品之后,剩余的钱就越少,就是最优解。于是转换成了一个01背包问题

在容量为 初始金额-m 的背包里尽可能装入价值较高的物品。
最后的结果=【总金额-最大价值物品-背包可装入的最大价值】

在背包部分其实已经很裸了,对于一件商品【排序后】,我们选择不买这件商品或买这件商品。
即MAX(dp[j],dp[j-a[i]]+a[i])
dp[j]表示拥有金额j时最多可以花掉多少钱

#include<stdio.h>///01背包的变形
#include<algorithm>
#include<string.h>
using namespace std;
int main()
{
    int i,j,n,m,a[1008],dp[1008];
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
        {
            return 0;
        }
        scanf("%d",&m);
        for(i=0;i<n;i++)///输入部分
        {
            scanf("%d",&a[i]);

        }
        if(m<5)///因为输入值m是小于1000的,因此当输入的本金少于5时什么都买不了,就直接输出原有金额就好
        {
            printf("%d\n",m);
            continue;
        }
        sort(a,a+n);///排序,按从小到大的顺序一个个购买,这样才能从基础部分一遍遍更新dp数组,因为在计算更大价钱的物品时,会需要买便宜物品的时记录的数据
        memset(dp,0,sizeof(dp));///记得清空数组
        for(i=0;i<n-1;i++)///背包部分,从第1个物品到第n-1个物品,因为最后一个物品是最大值,之后会单独计算,因此不能在背包中被统入
        {
            for(j=m-5;j>=a[i];j--)///从m-5的金额,开始记录,留出5元的最低金额,防止超出而不能消费掉最值钱的物品
            {///注意这个巧妙的限制条件,遍历的金额将一直到遍历物品的金额截止,也就是说不会把钱花超
                dp[j]=max(dp[j],dp[j-a[i]]+a[i]);///比较当前金额和加物品后的金额,谁比较大,就更新为谁,这里取的数据是当前 余额-物品金额 的位置锁存储的最大使用金额,最大使用金额再加上当前物品金额便是当前持有金额可以消费的最大值
            }
//            for(int k=m-5;k>=0;k--)///这里可以看出每次遍历,动态规划数组的更新路径
//            {
//                printf("%d\n",dp[k]);
//            }
//            printf("======\n");
        }///取出最贵的物品,留在背包完成后减去,是余额最少的
        printf("%d\n",m-dp[m-5]-a[n-1]);
    }
    return 0;
}

posted @ 2018-12-12 20:57  KuroNekonano  阅读(149)  评论(0编辑  收藏  举报