题意:n个物品,T个时刻,每个物品只能制作一次,制作需要ci的时间,在t时刻完成第i样物品可以得到ai−t∗bi的价值,问在T的时间内最多能得到多少价值。
详解见博客:https://www.cnblogs.com/BCOI/p/8999396.html
这道题长得很像01背包,但是单纯按01背包去做肯定是错的,因为哪样物品先做,哪样物品后做是会影响最终价值的,也就是与做的顺序有关。这里很容易想到一个贪心,就是将物品按b从大到小排。但是这个贪心方案是错误的,给这样一个样例:
74 3
502 100 402
2 4 1
43 20 11
输出为764,正确答案为785。
那我们要怎么排序呢?考虑i,j两件物品,在t时刻先选择i或j时的方案能够获得的价值。
先选择i: a[i] - b[i] * (t - c[j]) + a[j] - b[j] * t ①
先选择j: a[j] - b[j] * (t - c[i]) + a[i] - b[i] * t ②
若①>②,即先选i时价值更大,此时将①>②化简后,得应满足条件:
b[i] * c[j] > b[j] * c[i]
于是,贪心方案出炉了:按照b[i] * c[j]从大到小排序。
代码如下:
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<stack>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
ll dp[maxn];
struct food
{
ll a,b,c;
double d;
}f[55];
bool cmp(food x,food y)
{
return x.b*y.c>y.b*x.c;
}
int main()
{
int T,n;
cin>>T>>n;
for(int i=1;i<=n;++i) cin>>f[i].a;
for(int i=1;i<=n;++i) cin>>f[i].b;
for(int i=1;i<=n;++i) cin>>f[i].c;
sort(f+1,f+1+n,cmp);
mem(dp,0);
for(int i=1;i<=n;++i)
for(int j=T;j>=f[i].c;--j)
dp[j]=max(dp[j],dp[j-f[i].c]+f[i].a-j*f[i].b);
ll ans=0;
for(int i=0;i<=T;++i) ans=max(ans,dp[i]);
cout<<ans<<endl;
return 0;
}
PS:其实我本人是在看了题目标签中,有排序二字后,突然想到可以试试按照b/c从大到小排序,结果交了一遍真的AC了,然而表示并不知道为什么得这么做,是看了上面的博客才知道的。
小结:其实这道题的贪心方案推导也不算太难,只要动动脑,动动手,可能就能够搞出来。另外头一回遇到DP与贪心混一块的题,我还以为两者是对立的呢…