饭卡(01背包问题)

网上分析:

       设余额为m,令s=m-5,那么我们就要找使得容量为s的背包最后剩的空间最小的方法,找到之后再用这个剩余容量+5-最大的那个没有被选的商品价值就是最小余额.

但是现在我们不知道最后需要减的那个物品应该是哪个,可以证明最后需要减的那个物品一定是价值最大的那个.证明:

假设价值最大的为max,且我们假设存在最优的情况(使余额最小)下max物品不是最后一个被减的,最后一个被减的商品价值为mid,假设此时的余额为x.那么仔细想想如果我们把max和mid的位置互换,依然可以得到余额为x.(仔细想想是不是)

个人心得:这么经典简单的背包问题都懵逼了,真的是没谁了。不过确实脑瓜子不够机灵,你想呀,直接DP的花边界难确定还有负数,这样把5单独拿出来,

就可以完全转化为背包问题了,其实一开始拿出来后我还是很懵逼的,后面转念一想,拿出来后装最大值然后相减不就剩下的最小余额了吗,真的是一道好题。

真的是水得不行。

电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于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
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<iomanip>
 6 #include<algorithm>
 7 using namespace std;
 8 const int maxn=1005;
 9 int n,sum;
10 int cai[1005];
11 bool cmp(int a,int b)
12 {
13     return a>b;
14 }
15 int ways[1005];
16 int main()
17 {
18     while(cin>>n&&n!=0){
19            for(int i=1;i<=n;i++)
20                cin>>cai[i];
21                cin>>sum;
22                sort(cai+1,cai+n+1);
23                if(sum<5)
24                 cout<<sum<<endl;
25                else {
26                int ends=5-cai[n];
27                sum=sum-5;
28                memset(ways,0,sizeof(ways));
29                for(int i=1;i<=n-1;i++)
30                  for(int j=sum;j>=cai[i];j--)
31                      ways[j]=max(ways[j],ways[j-cai[i]]+cai[i]);
32                cout<<sum-ways[sum]+ends<<endl;
33                }
34 
35     }
36     return 0;
37 }


原文地址:https://www.cnblogs.com/blvt/p/7359829.html