首先我们设 f ( i , j , k ) f(i,j,k) f(i,j,k) 表示前 i i i 项还剩 j j j 元和 k k k 个冰淇淋的最大受欢迎程度。
得方程:
f
(
i
,
j
,
k
)
=
max
t
×
X
i
≤
k
,
t
+
C
i
≤
j
(
f
(
i
−
1
,
j
−
t
−
C
i
,
k
−
t
×
X
i
)
+
P
i
,
f
(
i
−
1
,
j
,
k
)
)
f(i,j,k)=\max_{t\times X_i\leq k,t+C_i\leq j}(f(i-1,j-t-C_i,k-t\times X_i)+P_i,f(i-1,j,k))
f(i,j,k)=t×Xi≤k,t+Ci≤jmax(f(i−1,j−t−Ci,k−t×Xi)+Pi,f(i−1,j,k))
简单分析,时间复杂度为
O
(
n
4
)
O(n^4)
O(n4) 级别的,无法通过本题。
我们可以感性理解一手,其实是因为我也没理解透,不难发现冰淇淋并不是另外一个需要考虑的量,而是一个可以代替钱的优惠券。
彬彬跟我聊天的时候给出了一个比喻:我们换钱的时候,总是考虑去汇率更大的银行换钱。
因此,这道题我们可以考虑以“贿赂”所需的冰淇淋的数量从小到大排序,前面的朋友等同于汇率更大的银行。
我们在前面用冰淇淋肯定比在后面用好。
简单证明一下,如果我们在后面用了冰淇淋,前面用了钱,可以将后面的冰淇淋换成钱,把前面的钱换成冰淇淋,这样钱数不变,花费的冰淇淋变少。
我们考虑枚举一个中间点,中间点以上全部使用冰淇淋,中间点以下全部使用钱,中间点两者皆可,求出最大受欢迎程度即可。
这就拆成了两个背包。
时间复杂度降为 O ( n 2 ) O(n^2) O(n2)。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=2e3+5;
struct node
{
LL p,c,x;
}a[N];
LL n,m,k,f[N][N],g[N][N],ans;
bool cmp(node x,node y)
{
return x.x<y.x;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld%lld",&a[i].p,&a[i].c,&a[i].x);
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
LL p=a[i].p,c=a[i].c*a[i].x;
for(int j=0;j<=c-1&&j<=k;j++)
{
f[i][j]=f[i-1][j];
}
for(int j=c;j<=k;j++)
{
f[i][j]=max(f[i-1][j],f[i-1][j-c]+p);
}
}
for(int i=n;i>=1;i--)
{
LL p=a[i].p,c=a[i].c;
for(int j=0;j<=c-1;j++)
{
g[i][j]=g[i+1][j];
}
for(int j=c;j<=m;j++)
{
g[i][j]=max(g[i+1][j],g[i+1][j-c]+p);
}
}
for(LL i=1;i<=n;i++)
{
for(LL j=0;j<=a[i].c&&j<=m;j++)
{
if((a[i].c-j)*a[i].x>k)continue;
ans=max(ans,f[i-1][k-(a[i].c-j)*a[i].x]+a[i].p+g[i+1][m-j]);
}
}
printf("%lld",ans);
return 0;
}