[SDOI2017]苹果树


题解

先扯点前置知识:
单调队列优化多重背包:
这玩意儿其实也可以用二进制拆分来优化
但是复杂度会多一个log
所以大致说一下单调队列怎么优化多重背包
假设物品个数为\(n\),每种重量为\(w_i\),数量为\(Num_i\),价值为\(val_i\),背包总容积为\(m\)
假设物品个数为\(n\),每种重量为\(w_i\),数量为\(Num_i\),价值为\(val_i\),背包总容积为\(m\)
那么有一个很简单的\(dp\)就是\(f[i][j] = \max( f[i][j] , f[i-1][j - k\times w_i]+k \times val_i )\)
那么我们可以考虑枚举倍数,也就是枚举这种物品装了几个
\(d=j\%w_i\)也就是余数,\(b=\min(Num_i,j/w_i)\)也就是选择这个东西的上限
那么我们可以从\(0 \sim w_i - 1\)枚举\(d\)
然后对于每一个\(d\)枚举倍数,可以发现这样形成的背包容量\(d+j\times w_i\)是相互独立且互不相同的
这样\(dp\)式子就成了\(f[i][d + j \times w_i] = \max( f[1][j] , f[i-1][d+k\times w_i] + (j - k) \times w_i )\)
这样可以发现就满足单调队列的形式了
对于每个\(j\)维护\(f_j-j\times w_i\)即可
再扯一句就是这个单调队列的话最好先放进去\(f_j-j\times w_i\)再更新
这样可以省去一维
大致代码长这样

for(int i = 1 , b ; i <= n ; i ++) {
        b = min( Num[i] , m / w[i] ) ;
        for(int d = 0 ; d < w[i] ; d ++) {
            int head = 1 , tail = 0 ;
            for(int j = 0 , temp ; j * w[i] + d <= m ; j ++ ) {
                temp = f[d + j * w[i]] - val[i] * j ;
                while(head <= tail && q[tail] <= temp) -- tail ;
                q[++ tail] = temp ; k[tail] = j ;
                while(head < tail && j - k[head] > b) ++ head ;
                f[d + j * w[i]] = max( f[d + j * w[i]] , q[head] + val[i] * j ) ;
            }
        }
    }

然后再来看这个题

猜你喜欢

转载自www.cnblogs.com/beretty/p/10672788.html