题目
题目描述: 小明是个大厨。他所在的餐厅每天早上都会买好n件食材(每种食材的数量可以视为无限),小明从到达餐厅开始就连续工作T时间。
每道菜肴的制作需要特定的一种食材以及一段时间,但是食材一旦放久就不新鲜了,菜的美味值会降低。
第i道菜肴有三个属性ai,bi,ci,ai是该菜肴的美味值,bi是该菜肴所选食材不新鲜的速率,如果在第t时刻完成第i道菜则美味值为:ai - t * bi,完成这道菜需要ci的时间。
小明希望在这T时间内能做出菜肴使得总美味值最大,所以小明求助于你。
输入描述 :
第1行输入三个整数n,m,T,分别代表食材种类,菜肴种类和工作时间。
第2行输入n个整数bi,代表第i个食材不新鲜的速率。
第3 - n + 2行,每行输入三个整数j,ai,ci,分别代表第i道菜肴需要的食材编号,菜肴的美味值,完成时间。
数据保证:0 < n,m ≤ 50,0 < j ≤ n,其他值均 < 1e6,美味值必须通过完整做出菜肴得到,数据保证在规定时间内至少能完整做出1道菜肴。
输出一行,一个整数,表示最大总美味值。
解析
这道题从题目可以看出这是一道01背包问题,但是和普通背包的差别就是:条件复杂了很多。
关于背包问题:
- 我们平常的背包问题经常会有:物品,数量(这个可能没有),权值,背包容量。
- 那这道题里面呢?我们知道了物品(本题的菜品),也有容量(本题的时间)。但是权值是用来衡量物品价值的,是什么呢?
- 是做菜耗时吗?不是!应该是每样菜对我们最终容量做出的贡献(在时间和品质上的贡献)。而要知道贡献就要用我们的数学知识了。
- 首先我们要知道是哪个菜的价值高,我们假设原本有 i 位置和 j 位置,假设先完成第 j 个菜的价值更大,
则有:(左边为先做 i 再做 j,右边相反) - 由此化简我们可以得到。也就是。(意思就是说:j 的价值大,这个比值就会小)
- 所以我们可以按照升序排列(保证价值大的在前面)。
然后又是写代码了:
- 按照题目要求,每个数组保存好。
- 按照题目要求,编辑一个cmp函数按照比值的升序排列(就会按照价值的降序排列,详情看上面的算法说明)
- 然后就是01背包的正常操作了。
- 最后选出最大的输出就好了。(有可能是负数,所以初始化都选-INF)
AC代码
#include <iostream> #include <algorithm> using namespace std; typedef long long ll; //代码预处理区 const int INF = 0x3f3f3f3f; const int MAX = 50 + 7; const int MAXT = 1e6 + 7; int id[MAX], a[MAX], c[MAX];//菜品编号,菜品美味值,菜品完成时间 int b[MAX], food[MAX];//食材腐败速率,需要的食材编号 ll dp[MAXT]; //全局变量区 template<class T>inline void read(T& res);//整型快读函数 int cmp(int x, int y);//条件排序函数 //函数预定义区 int main() { int n, m, T; read(n); read(m); read(T); for (int i = 1; i <= n; i++) read(b[i]); for (int i = 1; i <= m; i++) { read(food[i]); read(a[i]); read(c[i]); id[i] = i; } //数组初始化 sort(id + 1, id + m + 1, cmp); //按美味值贡献进行排序 dp[0] = 0; fill(dp + 1, dp + 1 + T, -INF); for (int i = 1; i <= m; i++) { int pos = id[i];//pos为菜品编号 for (int j = T; j >= c[pos]; j--) dp[j] = max(dp[j], dp[j - c[pos]] + a[pos] - j * b[food[pos]]); } //递推求解 ll ans = -INF; for (int i = 1; i <= T; i++) ans = max(ans, dp[i]); printf("%lld", ans); return 0; } template<class T>inline void read(T& res) { char c; T flag = 1; while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = -1; res = c - '0'; while ((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; res *= flag; } int cmp(int x, int y) { return c[x] * b[food[y]] < c[y] * b[food[x]]; } //函数区