HDU-2546 饭卡 (和放入顺序有关的01背包)

饭卡

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 33764    Accepted Submission(s): 11658


Problem Description
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
 

Input
多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。
 

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

Sample Input
  
  
1 50 5 10 1 2 3 2 1 1 2 3 2 1 50 0
 

Sample Output
  
  
-45 32
 

#include <bits/stdc++.h>
using namespace std;
int dp[1002], a[1002];
int main(){
    int n, m;
    while(scanf("%d", &n) != EOF && n != 0){
        for(int i = 1; i <= n; ++i){
            scanf("%d", &a[i]);
        }
        scanf("%d", &m);
        m -= 5;
        if(m < 0){
            printf("%d\n", m + 5);
        }
        else{
            memset(dp, 0, sizeof(dp));
            sort(a + 1, a + 1 + n);
            for(int i = 1; i < n; ++i){
                for(int j = m; j >= a[i]; --j){
                    dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
                }
            }
            printf("%d\n", m + 5 - dp[m] - a[n]);
        }
    }
}

/*
题意:某校饭卡只有大于等于5时才可以消费,1000种菜,每种菜的价格不超过50,希望最后卡上的钱剩余最少。

思路:
背包问题小变化,因为卡上需要大于等于5元才可以购买一种菜,我们不妨先将m减掉5,然后再做背包,在做背包的时候
我们不处理最大的那种菜,用之前减掉的5元和当前剩下的钱去买这个最贵的菜。不妨假设,在对除了最贵的所有菜
做背包之后还剩余x + 5元(5元是之前扣的),那么这个x一定小于剩下没买的所有菜的价格,否则我们背包时一定算进去了。
那么剩下的钱我们买哪种可以让价格最低呢,当然是最贵的了。
我们简单证明一下,对于一个可以把某一种解中所有菜都买到的价格,我们的购买顺序也是最贵的最后买的,否则未必可以买完
所有菜,比如某种解是5,6,7,我一共16元即可买到,如果先买6,7,剩余4元就完了,而我先买5,6,还剩5元正好买7。
这个可以从另一个问题中得到更好的理解,假设我需要买某一些菜,我最少需要准备多少钱?假设最后买的菜的价格为x,
其余菜价格总和为y,那么我一共需要min(x, 5)+y即可。那我一定让x越大越好, y越小越好是不是。
既然这样,无论哪种解,最贵的都是最后买(当然包括最优解),那么我们一定把全局最贵的菜放到解中,只要剩余5元就可以买到了,
然后让dp[m - 5]最大就好了。用其余的菜跑一个背包就行了。dp[j]表示j元最多可以买到多少菜,
转移方程:dp[j] = max(dp[j], dp[j - a[i]] + a[i])。
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值