饭卡问题(0-1背包的变形)
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
某天,食堂中有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元以下不可以点,让你求最后剩下的最少钱。
题目分析 :我们只要把5元单独出来,用这5元消费最大的那个值。剩下的0-1背包就可以了。
题目收获 :动态转移方程的变形
AC代码 :
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #define maxn 1005 using namespace std; int d[maxn]; bool cmp(int x, int y) { return x > y; } int main() { //freopen("in.txt", "r", stdin); int n,m; while (scanf("%d",&n)!=EOF && n) { int ni[maxn];//储存价格的数组 memset(d, 0, sizeof(d));//清空上一次计算的值 for (int i = 1; i <= n; i++) scanf("%d",&ni[i]); scanf("%d", &m); if (m < 5)//小于5元最直接输出 { printf("%d\n", m); continue; } sort(ni + 1, ni + 1 + n,cmp);//求最大值 int ans = ni[1];//储存最大值 ni[1] = 0;//最大者变为0 for (int i = 1; i <= n; i++)//0-1模板 { for (int j = m-5; j >= 1; j--) { if (j >= ni[i]) d[j] = max(d[j], d[j - ni[i]] + ni[i]);//转移方程有点点变化 } } printf("%d\n", (m - 5) - d[m - 5] + 5 - ans); } return 0; }