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
)