题意:在n块奶酪中选取若干块总高度不超过t的奶酪,使其价值最大。其中,高度在k以上的奶酪可以挤压它下面的奶酪,使他们的高度只有原来的4/5.
分析:首先,从题目中就可以看出这是一道完全背包的题目,但是由于高度大于k的奶酪会使它下面的奶酪高度变小,所以需要把状态拆分。所以我们可以在完全背包的基础上在加一层,用f[i][0]表示高度为i且存在大奶酪的最优解,用f[i][1]表示高度为i且不存在大奶酪的最优解。
对于没有大奶酪的情况我们可以直接用完全背包的状态转移方程搞定,但是在有大奶酪的情况下我们还需要考虑到大奶酪的位置,用贪心思想不难得出大奶酪一定要放在最上面才是最优解(可以挤压更多的奶酪),若是有两个以上的大奶酪,则要把高度最小的大奶酪放到最上面(都放在上面大奶酪不会被挤压到,高度不变,所以要把这个位置留给高度最小的大$奶酪)。
所以我们要把所有的大奶酪从小到大排列,先选大奶酪。一开始,所有的f[j][1]都要定义成无解,当第i个是大奶酪时,它的值就等于
f[(j−a[i].w)∗4/5][0]+a[i].v
(选取第i个奶酪,这样就有大奶酪了)。所以会有:
f[j][0]=max(f[j][0],f[j-a[i].w][0]+a[i].v) ;
f[j][1]=max(f[j][1],f[j-a[i].w*4/5][1]+a[i].v) ;(当f[j-a[i].w*4/5][1]存在解时)
f[j][1]=max(f[j][1],f[(j-a[i].w)*4/5][0]+a[i].v) ;(当第i个是大奶酪时)
最后的答案就是
max(f[m][0],f[m][1])
参考代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std ;
const int maxn=110,maxm=1010 ;
struct t{int v,w;}a[maxn] ;
bool cmp1(t a,t b)
{
return a.w>b.w ;
}
bool cmp2(t a,t b)
{
return a.w<b.w ;
}
int n,m,k,temp,f[maxm][2] ;
int main()
{
freopen("chess.in","r",stdin) ;
freopen("chess.out","w",stdout) ;
scanf("%d%d%d",&n,&m,&k) ;
for (int i=1;i<=n;i++)
scanf("%d%d",&a[i].v,&a[i].w) ;
for (int i=1;i<=m;i++) f[i][1]=-1 ;
sort(a+1,a+1+n,cmp1) ;
for (;a[temp+1].w>=k;temp++) ;
sort(a+1,a+1+temp,cmp2) ;
for (int i=1;i<=n;i++)
for (int j=a[i].w;j<=m;j++)
{
f[j][0]=max(f[j][0],f[j-a[i].w][0]+a[i].v) ;
if (f[j-a[i].w*4/5][1]!=-1) f[j][1]=max(f[j][1],f[j-a[i].w*4/5][1]+a[i].v) ;
if (a[i].w>=k) f[j][1]=max(f[j][1],f[(j-a[i].w)*4/5][0]+a[i].v) ;
}
printf("%d",max(f[m][0],f[m][1])) ;
return 0 ;
}