hdu2546 饭卡 _特殊的01背包

 

饭卡

 

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

 

 

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

 

 

Source

UESTC 6th Programming Contest Online

 

 

分析:要使卡上的余额最少,相当于用有限的金额去买最多的菜(总价值最大)。
题目有一个限制条件,就是“卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)”。
也是就是说,当卡上的金额大于等于5时,可以用5元去买任意价格的菜。
所以要预留5元,用来最后买最贵的菜,这样才能使最后的余额最小。
剩下的问题就是,求剩下的m-5元能买到的最高的价值总量,也就是一个单纯的01背包问题了。

 

 

//
// Created by Admin on 2017/3/13.
//

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int price[1010],dp[1010];
int main(){
    int n,m;
    while(scanf("%d",&n),n){
        for (int i = 0; i < n; ++i) {
            scanf("%d",&price[i]);
        }
        sort(price,price+n);  //排序是为了获得价格最高的菜,用预留的5元来购买
        scanf("%d",&m);
        if (m < 5){  //如果小于5元,无法购买任何菜,余额不变
            printf("%d\n",m);
            continue;
        }
        m-=5;  //求剩下m-5元所能买到的最高价值总量
        memset(dp,0, sizeof(dp));
        for (int i = 0; i < n-1; ++i) {  //m-5元最多能买n-1个菜,剩下的最贵的菜由预留的5元购买
            for (int j = m; j >= price[i]; j--) {  //如果饭卡余额j大于某个菜的价格,可以选择购买或者不购买这个菜;如果小于,则说明买不了这个菜,不执行操作
                if(dp[j-price[i]]+price[i]>dp[j])  //"dp[j-price[i]]+price[i]":购买   "dp[j]":不购买
                    dp[j]=dp[j-price[i]]+price[i];  //如果购买这个菜获得的价值比不购买要大,则选择购买,即dp[j]取两者中的较大值
            }
        }
        printf("%d\n",m+5-price[n-1]-dp[m]);  //输出饭卡所剩余额
    }
    return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值