HDUOJ_2546(饭卡)(背包问题)
饭卡
Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 16658 Accepted Submission(s): 5800
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。
n=0表示数据结束。
1 50 5 10 1 2 3 2 1 1 2 3 2 1 50 0
-45 32
提示:(思路摘自:hehe的博客(博客园))(注:本人也是看了这个博客,才明确了做题思路.思路简单易懂,同时也由于本人较懒,所以把思路复制粘贴了过来,并稍作修改)
(1)如果所有的菜的总金额sum小于等于卡上金额,我们可以购买所有的菜,并且直接输出余额。
(2)如果所有的菜,除去最贵的菜,的金额sum-max小于等于value-5,我们可以先购买其他的菜,最终卡上金额必大于等于5,最后购买那道最贵的菜,使总金额最低。同情况(1)可以直接输出余额,可与(1)条件合并。/*在下面的代码中,我没有考虑此种情况,考虑此种情况,可以节约程序运行时间*/
(3)如果卡上金额小于5,什么也买不了,直接输出余额。
(4)如果都不符合,那就进入最复杂的处理了。先购买适量的菜,使卡内余额的金额大于等于且最接近5(即用t去买其它的菜,尽可能花掉较多的钱(<=t), (t=m-5,m为饭卡最初的金额)最后购买最贵的菜(用sort排序找出最贵的菜)。子问题也就是01背包问题。
My solution:
/*2015.8.26*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int num[1100],f[1100];
int main()
{
int n,m,i,j,sum,t,min;
while(scanf("%d",&n)==1&&n)
{
sum=0;
memset(num,0,sizeof(num));
memset(f,0,sizeof(f));
for(i=1;i<=n;i++)
{
scanf("%d",&num[i]);
sum+=num[i];
}
scanf("%d",&m);
if(m<5)
{
printf("%d\n",m);/*金额不足5元时,不能买东西,数值原样输出*/
continue;
}
if(sum+5<=m)/*当所有菜的价钱之和小于卡里余额 m-5时,说明买完所有菜后卡里余额>=5,因此直接输出余额*/
{
printf("%d\n",m-sum);
continue;
}
t=m-5;/*卡里留5元,多余的钱用来买菜*/
sort(num,num+n+1);/*把所有的菜的价格进行排序,最后一个元素的价格是最大的,放在最后买*/
for(i=1;i<n;i++)/*去除价格最大的菜后,还剩下n-1种菜,接下来用多余的钱买这些菜*/
for(j=t;j>=num[i];j--)
{
f[j]=max(f[j],f[j-num[i]]+num[i]);
} /*f[t]存放的是在不超出t的情况下,花掉最多的钱数*/
min=m-f[t]-num[n];/*m是最初卡里的余额,f[t]是在余额不低于5的情况下,花掉的最大钱数,num[n]是价格最大的菜(放在最后买)*/
printf("%d\n",min);
}
return 0;
}