luogu1417 烹调方案
题目大意
一共有$n$件食材,每件食材有三个属性,$a_i$,$b_i$和$c_i$,如果在$t$时刻完成第$i$样食材则得到$a_i-t*b_i$的美味指数,用第$i$件食材做饭要花去$c_i$的时间。请设计烹调方案使得美味指数最大。
题解
这是一道典型的通过邻项交换进行贪心,随后得到动规方向的例子。如果没有$b_i$,这就是一个简单的01背包;有了$b_i$,我们可以尝试给物品排个序。
对于相邻的两个元素$i,j$,它们在$t$时刻开始做,它们不交换时,美味度和为$$a_i -b_i t+a_j -b_j(t+c_i)\tag 1$$,交换后,美味度和为$$a_j-b_j t+a_i -b_i(t+c_j)\tag 2$$。若要使正常的顺序使它们的和最大,则$$(1)-(2)=b_i c_j -b_j c_i>0$$。所以按照此排序后再01背包即可。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_OBJ = 60, MAX_V = 100010, MINF = 0xcfcfcfcf; struct Node { long long A, B, C; bool operator < (const Node& a) const { return a.B * C < B * a.C; } }_objs[MAX_OBJ]; int TotObj, TotV; long long DP() { static long long F[MAX_V]; memset(F, 0xcf, sizeof(F)); F[0] = 0; for (int i = 1; i <= TotObj; i++) for (int j = TotV; j >= _objs[i].C; j--) F[j] = max(F[j], F[j - _objs[i].C] + _objs[i].A - _objs[i].B * j); long long ans = 0; for (int i = 1; i <= TotV; i++) ans = max(ans, F[i]); return ans; } int main() { scanf("%d%d", &TotV, &TotObj); for (int i = 1; i <= TotObj; i++) scanf("%lld", &_objs[i].A); for (int i = 1; i <= TotObj; i++) scanf("%lld", &_objs[i].B); for (int i = 1; i <= TotObj; i++) scanf("%lld", &_objs[i].C); sort(_objs + 1, _objs + TotObj + 1); printf("%lld\n", DP()); return 0; }