烹调方案 (DP)

传送门

一道非常好的DP。看这个可能会觉得与01背包很像,不过这个的问题在于现做的菜肴会影响到后面的菜肴的价值。

我们在进行01背包DP时,一件物品的价值是不随着其被枚举的位置改变而改变的,但是这道题中不行。假设你现在有两种菜肴,你先做第一种会导致第二种的价值受损,反之第一种的价值就会受损,我们并不能立即知道哪种更优,而正常的01背包在DP的时候已经选过的物品就完全不再考虑了,所以如果这样的话,被更优的答案放在后面在本题中计算会出错。

那我们就有了另一种方法,把最优的放在前面,这样依次排序即可。具体怎么排?你选取两种状态自行列式两边消去后发现,对于状态x,y,如果有c[x]*b[y]<c[y]*b[x],那么x状态在前必然更优。所以我们按此排序之后做01背包即可。

看一下代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
typedef long long ll;
const int M = 50005;

ll read()
{
    ll ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

struct food
{
    ll a,b,c;
    friend bool operator < (const food &x,const food &y)
    {
        return x.c * y.b < y.c * x.b;
    }
}f[60];
ll t,n,dp[100005],maxn = -1;

int main()
{
    t = read(),n = read();
    rep(i,1,n) f[i].a = read();
    rep(i,1,n) f[i].b = read();
    rep(i,1,n) f[i].c = read();
    sort(f+1,f+1+n);
    rep(i,1,n)
      per(j,t,f[i].c) dp[j] = max(dp[j],dp[j-f[i].c] + f[i].a - j * f[i].b);
    rep(i,1,t) maxn = max(maxn,dp[i]);
    printf("%lld\n",maxn);
    return 0;
}

 

posted @ 2018-09-20 15:12  CaptainLi  阅读(219)  评论(0编辑  收藏  举报