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菜先做更好,交换后其他菜的贡献不变,因此可以有如下邻项交换证明:

\[\begin{aligned} \Sigma w + A_a - A_b\Sigma t + B_a - B_b(\Sigma t + A_c) + \Sigma w' &\leq \Sigma w + B_a - B_b\Sigma t + A_a - A_b(\Sigma t + B_c) + \Sigma w'\\ -A_cB_b &\leq -A_bB_c\\ \frac{A_b}{A_c} &\leq \frac{B_b}{B_c} \end{aligned} \]

注意到最终排序结果由自身属性决定,因此可以以此贪心排序消除后效性。

随后就是个普通的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;
}
posted @ 2022-08-12 22:28  空白菌  阅读(23)  评论(0编辑  收藏  举报