【题解】免费菜肴
美味菜肴
http://www.nowcoder.com/questionTerminal/312700f3777244fab148bfd95f2f5d59
这题是一个 01 背包的形式,唯一可能的解法是动态规划。
但在本题未经处理的情况下,动态规划是不能使用的。因为原图上的转移不是有向无环图,带来了后效性,使得动态规划出错。
于是我们需要安排一种转移顺序,使得原图的转移变成 DAG,从而应用动态规划解决问题。
结论是,一定存在一种最优方案 ,使得对于任意 ,都有 。
证明如下:若不满足该条件,则必然存在一个位置 ,满足 。
我们把 和 两道菜肴拎出来,单独计算它们对答案的贡献。
设这种方案中开始制作 的时间为 。
若按照 的顺序制作菜肴,则对答案的贡献为 。
如果我们交换 和 的顺序,变成按照 的顺序制作菜肴,则对答案的贡献为 。
作差法比较两贡献的大小关系,可以得到
由于 ,则 。也就是说,如果交换了 和 的顺序,可以使得答案变大,故而原方案不是最优方案,证毕。
于是我们可以将所有菜肴按照 从大到小排序,然后再从前往后做一遍 01 背包即可。
时间复杂度 。
代码:
#include <algorithm> #include <cstdio> #include <cstring> const int MaxN = 50, MaxM = 50, MaxT = 1000000; const long long NeInf = 0x8080808080808080; int N, M, T; int B[MaxN + 5]; int J[MaxM + 5], A[MaxM + 5], C[MaxM + 5]; int Lnk[MaxM + 5]; long long F[MaxT + 5]; void init() { scanf("%d %d %d", &N, &M, &T); for (int i = 1; i <= N; ++i) scanf("%d", &B[i]); for (int i = 1; i <= M; ++i) scanf("%d %d %d", &J[i], &A[i], &C[i]); } inline bool cmp(int x, int y) { return 1.0 * B[J[x]] / C[x] > 1.0 * B[J[y]] / C[y]; } void solve() { for (int i = 1; i <= M; ++i) Lnk[i] = i; std::sort(Lnk + 1, Lnk + 1 + M, cmp); memset(F, 0x80, sizeof F); F[0] = 0; for (int I = 1; I <= M; ++I) { int i = Lnk[I]; for (int j = T; j >= C[i]; --j) F[j] = std::max(F[j], F[j - C[i]] + A[i] - 1LL * B[J[i]] * j); } long long ans = NeInf; for (int i = 1; i <= T; ++i) ans = std::max(ans, F[i]); printf("%lld\n", ans); } int main() { init(); solve(); return 0; }