https://ac.nowcoder.com/acm/problem/14704
题意:
有n种食材,m道菜,T的时间去做菜。
每种食材无限,食材有一个不新鲜度b。每道菜有一个美味度a,每道菜所需食材的不新鲜度b,每道菜的做菜时间c。
做完每道菜可以得到一个美味度 = 美味度a-不新鲜度b*时间c。这里要注意,食材都是一开始就有的,即(不新鲜度*时间)这里的时间是食材存放的时间,而不是做菜时间,上面的例子只举例了一道菜所以是b*c。问在T时间内做的菜的美味度最多是多少?
(数据范围:n,m∈[1,50],其他∈[1,1e6])
思路:
很容易看出是在T时间内做 哪些比较划算的菜肴,使得最后的总美味值最大,每道菜做与不做,显然是dp,就像是01背包问题的取与不取。
但是每道菜在不同的时间做,得到的价值是不相同的,就不能直接套用01背包,不仅要考虑做与不做,还要考虑什么时候做。
举例某两道菜x和y,
先做x获得两道菜的美味度是ax-bx*cx + ay-by*(cx+cy),
先做y获得两道菜的美味度是ay-by*cy + ax-bx*(cx+cy)
规定要先做x的话,需要使得不等式成立
ax-bx*cx + ay-by*(cx+cy) > ay-by*cy + ax-bx*(cx+cy)
→ax - bxcx + ay - bycx - bycy > ay - bycy + ax - bxcx - bxcy
→bxcy > bycx
无论在何时做菜,只要满足上面这个不等式的做菜顺序就可以使得做出来的菜美味度最大。顺序确定了,就可以套用01背包了,二维滚成一维。
有一个注意点,bxcy取值可能都在1e6,int会爆,万事不决用longlong。
dp初始化-inf,dp[0]=0。
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<math.h> #include<string> #include<map> #include<queue> #include<stack> #include<set> #define ll long long #define inf 0x3f3f3f3f///负无穷小 -0x7f using namespace std; struct node { int a;///需要的食材编号 int w;///做出来的美味度 ll t;///做出来需要的时间 }; node c[55];///菜肴数 int n,m,T; ll b[55];///不新鲜速度 ll dp[1000005]; bool cmp(node x,node y)///对任意两个食材排序,损失美味度多的排在前面 { return b[ x.a ]*y.t > b[ y.a ]*x.t; } int main()///NC14074 贪心+背包问题 { scanf("%d%d%d",&n,&m,&T);///n种食材 m道菜 t时间去做 for(int i=1;i<=n;i++) scanf("%lld",&b[i]); for(int i=1;i<=m;i++) scanf("%d%d%lld",&c[i].a,&c[i].w,&c[i].t); sort(c+1,c+m+1,cmp); memset(dp,-0x7f,sizeof(dp)); dp[0]=0; for(int i=1;i<=m;i++) { int idx=c[i].a; for(int j=T;j>=c[i].t;j--) dp[j]=max(dp[j], dp[ j-c[i].t ] + c[i].w-j*b[idx] );///j*b[idx]可能爆int } ll ans=-1e18; for(int i=1;i<=T;i++) ans=max(ans,dp[i]); printf("%lld ",ans); return 0; }