电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于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
解题报告:
这道题也可以说是刚刚接触背包问题的小菜鸟必做的一道题了。解出这道题的关键在于怎么处理这个5元。我们的处理方式是,对余额-5 并将最贵的菜踢出去,然后对这个新余额和剩余的n-1个物品进行0-1背包,解出能购买的最大价值。
这种做法的正确性在此稍作说明:
首先因为我们需要余额最小,所以直观上我们肯定想把最贵的放在最后一个买,这样直接拉开差距。但是有的同学会反过来想,那我万一是把最贵的物品放到背包里面去跑,然后这样可能更能填满呢,(即最能使余额接近5元)然后此时我再拿一个第二贵的去使余额变为负数,这样有可能更好啊。。嗯,这样想是没有问题的,但是你要这么想啊,既然你这样,那还是说明用到了最贵的物品,只是放到背包中了,其实相当于背包中放物品的次序换了一下,该放的物品还是那些物品,所以并不会影响最终的答案啊,而你如果在n-1个物品背包的时候没有用到最贵的物品,那何必要让第二贵的去当最后一个使余额变负数的物品呢?用最贵的岂不是更好?
综上,我们得出结论,把最贵的物品贪心出来,然后对剩下的物品进行0-1背包的做法是正确的。
为了加深对本题的理解,这里附上三种方法,三种代码:
AC代码1:(普通的0-1背包)
#include
#include
#include
using namespace std;
int n,m;
int v[1000 +5];
int dp[1000 + 5];
int main()
{
while(~scanf("%d",&n) ) {
if(n == 0 ) break;
memset(dp,0,sizeof(dp));
for(int i = 1; i<=n; i++) {
cin>>v[i];
}
cin>>m;
sort(v+1,v+n+1);
m-=5;
if(m < 0) {
printf("%d\n",m+5);
continue;
}
for(int i = 1; i=v[i]; j--) {
dp[j] = max(dp[j], dp[j - v[i] ] + v[i]);
}
}
// printf("**%d***%d\n",m,dp[n-1],dp[n]);
printf("%d\n",m+5 - dp[m] - v[n]);
}
return 0 ;
}
AC代码2:(当成恰好花费做的)
#include
using namespace std;
int dp[1000 + 5];
int v[1000 + 5];
int n,m;
int main()
{
while(~scanf("%d",&n) ) {
if(n == 0 ) break;
for(int i = 1; i<=n; i++) {
scanf("%d",&v[i]);
}
scanf("%d",&m);
m-=5;
sort(v+1,v+n+1);
int tmp = v[n];
if(m < 0) {
printf("%d\n",m+5);continue;
}
for(int i = 1; i<=1001; i++) dp[i] = - 0x3f3f3f3f;//这里刚开始写成i<=2000,把v都覆盖成了-INF,所以直接错了,要注意啊!从汇编的角度也可以理解这件事情。。。自己想想吧。,
dp[0]=0;
for(int i = 1; i=v[i]; j--) {
dp[j] = max(dp[j],dp[j-v[i]] + v[i]);
}
}
int res = 0;
for(int i = m; i>=0; i--) {
if(dp[i]>=0) {
res = dp[i];break;
}
}
printf("%d\n", m + 5 -res - tmp );
}
return 0;
}
AC代码3:(因为单个物品的最大花费是50元,所以我在余额上统一+50元,最后在减去这50元,这做法就不需要排序贪心了)
#include
#define nn 1100
#define inff 0x3fffffff
using namespace std;
int n,m;
int a[nn];
bool dp[nn];
int main() {
int i,j;
while(scanf("%d",&n),n) {
for(i=1; i<=n; i++) {
scanf("%d",&a[i]);
}
scanf("%d",&m);
sort(a+1,a+n+1);
memset(dp,false,sizeof(dp));
dp[m+50]=true;
for(i=1; i<=n; i++) {
for(j=0; j<=m+50; j++) {
if(j+a[i]-50>=5&&j+a[i]<=m+50)
dp[j]=dp[j+a[i]]?true:dp[j];
}
}
for(i=0; i<=m+50; i++)
if(dp[i])
break;
printf("%d\n",i-50);
}
return 0;
}