# 多重背包问题 II ###### tags: `競程題解`,`競程` 題敘: ![](https://i.imgur.com/AJ2mOs7.png) 如果硬幹的話 把每一個物件切分成1個一個下去丟背包 那複雜度會是 $O(N*2000*V)$ 大約在 $O(4e9)$ 左右 非常危險 但我丟上去 甚至沒加 IO 還是 AC 了 XD 那要怎麼想呢 很簡單 把每個物件拆成 2^n 個就好了 例如 a 物件有 11 個 那麼我們就把他拆成 1+2+4+4 個 然後下去丟背包 可以稍微嘗試一下 1,2,4,4 是可以組出 1~11中的任意數的 更深入的解釋: 拿剛剛的例子 11 為什麼不能拆成 1 2 4 8 呢 很簡單 因為假設選 4,8 好了 那就會超過題目給的11 那就不行了 那要怎麼做呢 ```cpp= int n=1; while(k-n>=0){ k-=n; pb.push_back(n); n*=2; } if(k)pb.push_back(k); ``` 這樣的話 11 就會被拆成 1,2,4,4 可以看到 1,2,4 可以組合成 1~7中"任意"一個數 加上四後 如果想組出 10 那麼 就先從剛剛的 1,2,4 中 組出 6 然後直接加最後的4即可 其他大於7的數也是同理。 如果上面的還是不太懂的話,可以看一下另一個解釋方法 拆成 1,2,4,4 後 只要是想要組出 >7的數 那就直接把最後弄出來的4減掉 剩下的數一定小於等於7 1,2,4就可以隨便把7組合出來了! ```cpp= #include "iostream" #include "vector" using namespace std; int main(){ int dp[10010]={}; int n,v,val,w; cin>>n>>v; int ans=0; for(int j=0;j<n;j++){ int k; cin>>w>>val>>k; vector<int> pb; int n=1; while(k-n>=0){ k-=n; pb.push_back(n); n*=2; } if(k)pb.push_back(k); for(int j:pb){ for(int i=v;i>=w*j;i--){ if(dp[i]<dp[i-w*j]+val*j){ dp[i]=max(dp[i],dp[i-w*j]+j*val); } ans=max(ans,dp[i]); } } } cout<<ans; } ``` 後來晚上剛好翻到資芽的講義 ![](https://i.imgur.com/PIcfnYY.png)