1. 程式人生 > 其它 >ARC073B Simple Knapsack

ARC073B Simple Knapsack

upd - 2021.8.8 : 修正筆誤。

這題其實沒有想象中的那麼複雜。

樓上兩位的題解都有一點麻煩了。

所以提供一種簡單易懂的方法。


這道題實質上就是個01揹包。

但是資料範圍:\(W \leq 10^9\) , \(w_i \leq 10^9\)

1e9的01揹包您開心麼?(

很明顯普通的01揹包是不闊以的。

注意到題面裡有這樣一句話:

For each \(i =2,3…,N\)\(w_1\leq w_i \leq w_1+3\)

既然這題就是01揹包。

那我們考慮如何“縮小”這個資料範圍。

既然 \(w_i\) 是一定大於等於 \(w_1\)的。

我們乾脆就直接給 所有\(w_i\)減去一個 \(w_1-1\)

這裡為了避免 \(w_1=0\) 所以是\(w_1-1\)

然後我們的揹包容積也會改變。

這時候對於每個物品的最大容積就是 \(\sum^n_{i=1} (w[i]-(w[1]-1))\)

\(f[item][size]\) 表示用 \(item\)個物品,用了\(size\) 空間的最大價值。

那麼我們考慮轉移。

因為這裡揹包真實的容積還是 \(m\)

所以我們在決策的時候要看這個狀態在原來的情況下是否合法。

所以考慮之前用了 \(j\) 個東西,現在對於物品 \(i\) 用了 \(k\)的空間。

當前的狀態如果有效。

那麼就需要滿足 \(j\times(w[1]-1)+k \leq m\)

就相當於你現在對於那個物品所用的空間,加上之前所有減去的空間,能夠在原來的揹包裡面放下。

因為我們的 \(j\) 是不確定的。

所以要多開一層迴圈來列舉。

這部分的程式碼如下:

    for(register int i=1;i<=n;++i){
        for(register int j=i;j>=1;--j){//倒序列舉是為了後面的轉移
            for(register int k=sum;k>=w[i];--k){//sum 是我們說的對於每個物品的最大容積。
                f[j][k]=max(f[j][k],f[j-1][k-w[i]]+v[i]);
                if(j*(w[1]-1)+k<=m){
                    res=max(f[j][k],res);
                } //狀態合法。
            }
        }
    }

可能會有點繞。

你可以把它理解成對於每個物品都進行一次 01揹包的形式的決策和轉移。


那麼到這裡,這道題就算完結了。

不過還要記得開long long(在狀態是否合法那裡還有一些地方都會爆int

完整程式碼可以看:https://www.luogu.com.cn/paste/ajoe2gr0