美味菜肴
题目描述:
小明是个大厨。他所在的餐厅每天早上都会买好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道菜肴。
输出描述 :
输出一行,一个整数,表示最大总美味值。
思路:
一定的时间做一定的菜,每个菜不能分割,很容易发现是个0/1背包问题,,但是因为价值跟所完成的时间有关(即跟选取顺序有关),所以不能按照顺序直接背包,要先排序,即做菜的顺序也很重要,虽然一定时间做了一些菜,但是做的顺序不一样答案也可能不一样。
假设i、j两道菜,应该先完成菜i:
0/1背包
1.dp[i][j]dp[i][j] ,表示时间j内做前i到菜的最大价值,也可以用滚动数组来写,就是一维dp[j]dp[j] ,此时j应该反过来循环,从后往前覆盖。
2.虽然价值不会超过正的1e9,但是会比-1e9还小,所以要开ll,而且dp[j]dp[j] 除了dp[0]dp[0] 都要初始化为-1e18。
3.普通的0/1背包最后的dp[v]dp[v] 就是答案,但是为什么这里还要找呢,因为这题时间越多会减少价值,所以dp[T]dp[T] 不一定是答案。
Code:
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
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;
}
ll dp[maxn];
ll b[55],x;
struct node{
ll a,b,c;
bool operator<(const node&a)const{
return b*a.c>c*a.b;
}
}a[55];
int n,m,t;
int main() {
read(n),read(m),read(t);
for(int i=1;i<=n;++i) read(b[i]);
for(int i=1;i<=m;++i) {
read(x),read(a[i].a),read(a[i].c);
a[i].b=b[x];
}
sort(a+1,a+1+m);
fill(dp+1,dp+1+t,-0x3f3f3f3f3f3f3f3f);
for(int i=1;i<=m;++i)
for(int j=t;j>=a[i].c;--j)
dp[j]=max(dp[j],dp[j-a[i].c]+a[i].a-a[i].b*j);
ll ans=-0x3f3f3f3f3f3f3f3f;
for(int i=1;i<=t;++i) ans=max(ans,dp[i]);
printf("%lld\n",ans);
}