题目见JZOJ 4743
解题报告:
10%:ans=max(a,b,c)
40%:暴力不讲
100%:状压dp
设状态f[s,i,j=1/2/3]表示当前状态为s(s为二进制状态,第i位为0表示第i个积木还没选,1则表示选了),在最上面的积木编号为i,j表示那块积木选择的高是输入时该积木对应的a、b还是c。j=1则表示选择选的是a,j=2则表示选的是b,j=3则表示选的是c。转移就很显然了[f[s+2^(k-1),k,j']=max(f[s,i,j]+a[k,j'])并保证第k块积木长宽都比第i块积木小或相等。
反思与总结:
这道题本来是一道水题,但在比赛中我竟然没切掉,说以我觉得还是值得反省的。首先,我对状压dp还不是很熟,遇到这种题不是很敏感,下面我总结一下状压dp的一些特点:
首先 一般状压的题n都很小,一般n<=20;其次它是用来解决物品可以以任意顺序扔进背包但放物品的顺序对答案有影响的背包问题,如这题积木可以以任意顺序堆起,但积木必须保证长与宽均严格不上升,也就是调换两个积木顺序就不合法了。
其实我一直很好奇,为什么搜索时间复杂度会比dp大这么多呢?后来我搞明白了,主要是搜索时搜了太多重复的东西,而dp就相当于记忆化搜索,把之前得到的东西记录下来,效率明显高很多。听起来有点抽象,下面举个例子:
有n张卡片排成一行,每张卡片上可以填上一个1~m的正整数,但要求右边的要比左边填的数大,问一共有多少种填法(答案mod 100000007)?这道题当然可以搜索做,枚举每张卡片填什么,但时间复杂度为O(ans),也就是答案越小程序越快,当n,m都很小时,答案也很小,还能过,但当n,m很大时,比如n=500,m=1000时,答案就非常大了,搜索肯定过不了,那我们就只能dp,f[i,j]=sigema(f[i-1,k])(k<j),当然我们还可以搞个辅助数组g[i,j]=sigema(f[i,k])(k<=j),那方程就变成f[i,j]=g[i-1,j-1],g[i,j]=g[i,j-1]+f[i,j].时间复杂度O(nm),为什么效率这么高呢?假设第i张卡片要填的是x,dp就可以利用前面得出的结果直接算出第i张卡片填x时的方案数,而搜索还要再枚举前面i-1张填什么,效率肯定要慢了。
dp优于搜索因为dp更聪明,懂得利用以前的结果,会总结。而搜索只会傻呆呆的把全部可能的情况都枚举完,只懂死搜,不懂总结。所以我们学习也是一样,要学习dp这种不断总结、不断反思的精神,把学习中的心得记录下来,以求下一次可以利用它。写博客就是一个很好的方法,我现在写博客频率还太少了,所以我以后也要多多写博客。