[洛谷P1417] 烹调方案

洛谷题目链接:烹调方案

题目背景

由于你的帮助,火星只遭受了最小的损失。但gw懒得重建家园了,就造了一艘飞船飞向遥远的earth星。不过飞船飞到一半,gw发现了一个很严重的问题:肚子饿了~

gw还是会做饭的,于是拿出了储藏的食物准备填饱肚子。gw希望能在T时间内做出最美味的食物,但是这些食物美味程度的计算方式比较奇葩,于是绝望的gw只好求助于你了。

题目描述

一共有n件食材,每件食材有三个属性,ai,bi和ci,如果在t时刻完成第i样食材则得到ai-t*bi的美味指数,用第i件食材做饭要花去ci的时间。

众所周知,gw的厨艺不怎么样,所以他需要你设计烹调方案使得美味指数最大

输入输出格式

输入格式:

第一行是两个正整数T和n,表示到达地球所需时间和食材个数。

下面一行n个整数,ai

下面一行n个整数,bi

下面一行n个整数,ci

输出格式:

输出最大美味指数

输入输出样例

输入样例#1:

74 1
502
2
47

输出样例#1:

408

说明

【数据范围】

对于40%的数据1<=n<=10

对于100%的数据1<=n<=50

所有数字均小于100,000

【题目来源】

tinylic改编


一句话题意: (n)个物品,(T)个时刻,每个物品只能制作一次,制作需要(c_i)的时间,在(t)时刻完成第(i)样物品可以得到(a_i-t*b_i)的价值,问在(T)的时间内最多能得到多少价值.

一眼看来显然是一个裸的01背包,然而这样是不对的...

那么为什么这样不对呢?我们想一下,如果直接按01背包的取法,在枚举的时候物品是按顺序枚举的.但是显然先枚举到一个物品,就无法计算该物品在它之后的物品取的价值了.比如有两个物品(i,j).先取(i)物品,那么就不能算先取(j)再取(i)的价值了(想一下为什么).

所以如果想要直接按01背包取物品的话,就要保证先取的排在前面枚举的物品能得到最大的价值.

因为(b_i)会让取一个物品的价值不断减小,所以很容易想到一个错误的贪心:按照(b_i)排序.然而是错误的,因为这样排序出的结果也有可能不满足条件.

所以我们考虑来如何选最优方案.假设在(t)时刻要作选择(i,j)两个物品中哪个先取,那么它们得到的价值分别是:

[a_i-b_i*(c_i+t)+a_j-b_j*(c_i+c_j+t)$$ 和 $$a_j-b_j*(c_j+t)+a_i-b_i*(c_i+c_j+t) ]

如果先取(i)物品得到更高的总价值,则有: $$a_i-b_i(c_i+t)+a_j-b_j(c_i+c_j+t)>a_j-b_j(c_j+t)+a_i-b_i(c_i+c_j+t)$$
化简得到:

[b_j*c_i < b_i*c_j ]

这样就得到了对物品排序的方法.

所以最后先按这个排序,再做一遍01背包就可以了.
最后记得开long long!!!

#include<bits/stdc++.h>
using namespace std;
const int N=50+5;
const int MAXN=100000+5;
typedef long long lol;

lol T, n, f[MAXN], ans = 0;

struct meal{
    lol a, b, c;
}t[N];

bool cmp(meal i,meal j){
    return j.b*i.c < i.b*j.c;
}

int main(){
    //freopen("data.in","r",stdin);
    cin >> T >> n;
    for(lol i=1;i<=n;i++) cin >> t[i].a;
    for(lol i=1;i<=n;i++) cin >> t[i].b;
    for(lol i=1;i<=n;i++) cin >> t[i].c;
    sort(t+1 , t+n+1 , cmp);
    memset(f,128,sizeof(f)); f[0] = 0;
    for(lol j=1;j<=n;j++)
	    for(lol i=T;i>=t[j].c;i--){
	        f[i] = max(f[i-t[j].c]+t[j].a-t[j].b*i,f[i]);
	        ans = max(ans , f[i]);
	    }
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/BCOI/p/8999396.html