P4138 [JOISC2014] 挂饰 题解

· · 题解

0x00 思路

先看题。

JOI君有 N 个装在手机上的挂饰,编号为 1 ~ N 。 JOI君可以将其中的一些装在手机上。JOI君的挂饰有一些与众不同——其中的一些挂饰附有可以挂其他挂件的挂钩。每个挂件要么直接挂在手机上,要么挂在其他挂件的挂钩上。直接挂在手机上的挂件最多有 1。此外,每个挂件有一个安装时会获得的喜悦值,用一个整数来表示。如果JOI君很讨厌某个挂饰,那么这个挂饰的喜悦值就是一个负数。JOI君想要最大化所有挂饰的喜悦值之和。注意不必要将所有的挂钩都挂上挂饰,而且一个都不挂也是可以的

对于每一个挂件,我可以选择挂或不挂,那么由于每个挂件只有 1 个,所以这是一个 01 背包。

可是这又与 01 背包有一点区别:每一个物品多了一个属性:挂钩数,因此为了适应这个多出来的属性,我们决定把它变成背包容量。

接着因为我们认为,挂钩越多越有用(因为能挂的挂件越多欢喜值越大(都是整数的情况下)),所以我们按照挂钩的数量从大到小排序。

0x01 定义状态

对于 d[i][j],我们定义:挂完或选择不挂后i 个挂件后还剩余 j 个空挂钩所得的最大喜悦值。

0x02 状态转移方程

那么很容易,我们可以发现有 2 种情况:

  1. 不选用它

    这个状态很简单,由于是 01 背包,所以 d[i][j]=d[i-1][j]

  2. 选用它

    这个状态有 2 种情况。

    一种是 j \geq A_i : 那么此时的 d[i][j] 就等于 d[i-1][j-A_i+1]+B_i (这个挂件需要 1 个挂钩)。

    另一种是 j<A_i : 由于我们挂了这个挂件,所以必须得有一个挂钩来挂这个挂件,而此时 j-A_i+1<0,所以我们强行认为它是 1

所以状态转移方程就是 d[i][j]=\max(d[i-1][j],d[i-1][\max(j-A_i,0)+1]+B_i)

0x03 注意优化!

为什么呢? 由于 $A_i$ 可能为 $0$ ,而 $j>0$,所以可能会用到 $d[i-1][j+1]$ 的值,而用一维数组的话 $j+1$ 已经被赋成 $d[i][j+1]$ 了,所以只能用 $2$ 行。 # code ```c #include<bits/stdc++.h> using namespace std; int d[2005][2005]; struct gs { int gg,hxz; }a[2005]; bool cmp(gs b,gs c) { return b.gg>c.gg; } int main() { int n,m,i,j,ans=0; scanf("%d",&n); memset(d,128,sizeof(d)); d[0][1]=0; for(i=1;i<=n;i++) scanf("%d %d",&a[i].gg,&a[i].hxz); sort(a+1,a+1+n,cmp); for(i=1;i<=n;i++) for(j=0;j<=n;j++) d[i][j]=max(d[i-1][j],d[i-1][max(j-a[i].gg,0)+1]+a[i].hxz),ans=max(ans,d[i][j]); printf("%d",ans); return 0; } ```