#2020.02.04训练题解#背包入门(B题)

题源HDU-2546

HDU-2546-饭卡

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

题意

  • 有多组数据,每组第一行输入一个N,代表有N种菜
  • 第二行是N种菜各自的价格,第三行是卡上的余额
  • 直到N为零程序结束
  • 卡上剩余金额大于等于5元即可购买,即使购买后卡上余额为负数
  • 卡上剩余金额小于5元不可购买,即使剩余金额足够买某些菜
  • 每种菜只能被购买一次
  • 对于每组数据求卡上最少的余额是多少

题解

  • 这也是一个01背包模板题
  • 注意循环多组输入到N==0退出
  • 不同的是,这题有金额5的分界,以及余额可为负数,输出剩余金额
  • 那么不妨 调用knapsack_01()前先将v(卡上余额)减5
  • 那么在knapsack_01()函数内部,卡上金额不可为负数,则和普通01背包一样,全部金额都可用于购买
  • 还要注意的是!!!要使卡上余额最少
  • 在尽可能花光减了5后的所有金额后,仍保留了至少5元的底数
  • 可以凭着剩余的金额再买一份菜,这份菜越贵越好
  • 所以调用knapsack_01()前可对所有价格进行sort排序
  • 保留最大不参与循环(若升序排序,则即为第n个)
  • 最后要返回剩余金额的时候,v+5-cost[n]-dp[v]
  • 即加回5,减去最贵的,减去想尽可能花光预留5元后其余金额的实际花费不一定真的花光了

涉及知识点

  • 背包 算法(此题属于01背包)
  • 对于背包的算法-详见链接博客介绍背包

AC代码

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e3+10;
int T,n,v,value[maxn],cost[maxn],dp[maxn]; 
int knapsack_01()
{
	memset(dp,0,sizeof(dp));
	for(int i=n-1;i>=1;--i)//for(int i=1;i<=n-1;++i),外层循环正序逆序两者均可
	//排序值为找最大,且最大不参与遍历,其他可当做乱序一个个遍历即可
	{//遍历到i,就表示遍历到第i个物品,优化二维数组变为一位数组 
		for(int j=v;j>=cost[i];--j)//从大到小遍历!!!
		/*
			以防同一个物品从小到大的话
			先把小体积优化好,再优化大体积
			那么大体积用的小体积dp可能是放这个物品优化过的
			但01背包,每个物品只有一个,不可重复放 
		*/ 
		{
			if(cost[i]<=j) dp[j]=max(dp[j],dp[j-cost[i]]+cost[i]);
		}//dp[v]即用到只剩5元能买多少钱的东西,从第二贵的开始,最贵的只剩5元的时候买 
	}
	return v+5-cost[n]-dp[v];
}
int main()
{
	while(scanf("%d",&n)&&n)
	{
		memset(cost,0,sizeof(cost));
		for(int i=1;i<=n;++i) scanf("%d",&cost[i]);
		scanf("%d",&v);
		if(v<5)
		{
			printf("%d\n",v);
			continue;
		}
		v-=5;
		sort(cost+1,cost+n+1);
		printf("%d\n",knapsack_01());
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值