类型:01背包
策略就是把最大的留起来,剩下的尽量去买到5块钱,最后用最大的买。
留最大不用说。尽量买到5块钱就是一个背包问题。
即 背包容量为m-5
每个物品的容量=价值=菜的价格
特殊判断:如果m < 5直接输出就可以
证明策略正确性:
假设最后买的不是最大的,设为b.最大的设为a. 其他设为C
有(C+a)这个组合尽量买到5块钱 + b > (C+b)->5块钱 + a
1: C+a <= m-5
则 C+b <=m-5
此时 C+a+b = C+b+a 假设不成立
2: C+a > m-5
则假设Ci + a 是其尽量买到5块钱的最优组合
因为 Ci+b < m-5 (Ci + b 不一定是最优组合, 但是可行组合)
有 Ci+a+b = Ci+b+a 假设不成立
综上,假设不成立
所以,原策略成立。
附代码:
HDU 2546 饭卡
/************************************************************************* > File Name: hdu2546.cpp > Author: Shine > Created Time: 2013-05-10 上午 9:36:14 > QuestionType: 01背包 > Way: 用01背包 求用除了最大的那个数之外 其他的数所能组成的最接近m-5的数 > Submit: 2 1WA(m-5 想成 m-4) 1AC > Gain: 01背包就是 有一个限制 最后价值尽量大 的某个东西拿或不拿的问题。 > Experience: 无论如何,想几个简单的测试数据,测试一下再提交。 ************************************************************************/ #include <cstdio> #include <cstring> #include <cstdlib> #define max(a,b) ((a)>(b)?(a):(b)) int w[1010], c[1010], f[1010]; int cmp(const void *elem1, const void *elem2) { int *p1 = (int *)elem1; int *p2 = (int *)elem2; return *p1-*p2; } int main() { int n; while (scanf("%d", &n) && n) { int i; for (i = 0; i < n; i++) { scanf("%d", &c[i]); w[i] = c[i]; } int m; scanf("%d", &m); if (m < 5) { printf("%d\n", m); continue; } qsort(c, n, sizeof(c[0]), cmp); qsort(w, n, sizeof(w[0]), cmp); memset(f, 0, sizeof(f)); for (i = 0; i < n-1; i++) { int j; for (j = m-5; j >= c[i]; j--) { f[j] = max(f[j], f[j-c[i]]+w[i]); } } printf("%d\n", m-(f[m-5]+w[i])); } return 0; }