NC14704 美味菜肴
题目
题目描述
小明是个大厨,早上起来他开始一天的工作。他所在的餐厅每天早上都会买好 \(n\) 件食材(每种食材的数量可以视为无限),小明从到达餐厅开始就连续工作 \(T\) 时间。每道菜肴的制作需要特定的一种食材以及一段时间,但是食材一旦放久就不新鲜了,菜的美味值会降低。第 \(i\) 道菜肴有三个属性 \(a_i,b_i,c_i\) ,\(a_i\) 是该菜肴的美味值,\(b_i\) 是该菜肴所选食材不新鲜的速率,如果在第 \(t\) 时刻完成第 \(i\) 道菜则美味值为:\(a_i-t*b_i\),完成这道菜需要 \(c_i\) 的时间。小明希望在这 \(T\) 时间内能做出菜肴使得总美味值最大,所以小明求助于你。
输入描述
第1行输入三个整数 \(n,m,T\) ,分别代表食材种类,菜肴种类和工作时间。
第2行输入 \(n\) 个整数 \(b_i\) ,代表第 \(i\) 个食材不新鲜的速率。
接下来的m行,每行输入三个整数 \(j,a_i,c_i\) ,分别代表第 \(i\) 道菜肴需要的食材编号,菜肴的美味值,完成时间。
数据保证:\(0<n,m≤50,0<j≤n\) ,其他值均 \(<10^6\) ,美味值必须通过完整做出菜肴得到,数据保证在规定时间内至少能完整做出1道菜肴。
输出描述
输出一行,一个整数,表示最大总美味值。
示例1
输入
1 1 74
2
1 502 47
输出
408
示例2
输入
2 2 10
2 1
1 100 8
2 50 3
输出
84
备注
最大总美味值可能为负。
题解
知识点:贪心,背包dp。
注意到贡献是会被选择顺序影响,有后效性,不能直接dp。尝试用排序找到最优相对位置消除后效性。假设A菜先做比B菜先做更好,交换后其他菜的贡献不变,因此可以有如下邻项交换证明:
注意到最终排序结果由自身属性决定,因此可以以此贪心排序消除后效性。
随后就是个普通的01背包,注意至少要做一个菜,因此要初始化负无穷,避免一个菜都不选的情况,因此最后也要遍历区间 \([1,t]\)找最大值。
时间复杂度 \(O(m\log m +mt+n)\)
空间复杂度 \(O(n+m+t)\)
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll b[57];
struct node {
ll a, b, c;
}cai[57];
ll dp[1000007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m, t;
cin >> n >> m >> t;
for (int i = 1;i <= n;i++) cin >> b[i];
for (int i = 1;i <= m;i++) cin >> cai[i].b >> cai[i].a >> cai[i].c, cai[i].b = b[cai[i].b];
///由于时间会导致美味度变化,所以做菜顺序有后效性,因此贪心排序为最优顺序
sort(cai + 1, cai + m + 1, [&](node A, node B) {return A.b * B.c >= B.b * A.c;});
memset(dp, -0x3f, sizeof(dp));///因为不能不做,所以不放空气
dp[0] = 0;
for (int i = 1;i <= m;i++) {
for (int j = t;j >= cai[i].c;j--) {///不是完全背包
dp[j] = max(dp[j], dp[j - cai[i].c] + cai[i].a - j * cai[i].b);
}
}
ll ans = -1e18;
for (int i = 1;i <= t;i++) ans = max(ans, dp[i]);///i≠0
cout << ans << '\n';
return 0;
}
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16581603.html