日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【学习笔记】多重背包相关优化——二进制优化/单调队列优化

發布時間:2023/12/3 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【学习笔记】多重背包相关优化——二进制优化/单调队列优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

多重背包——二進制優化/單調隊列優化

  • 二進制優化
  • 單調隊列優化

代碼都是 POJ1742 的,注意,那道題二進制優化會超時。

普通的多重背包式子,物品個數限制:c[i]c[i]c[i],單個物品價值 w[i]w[i]w[i],每個物品的體積 v[i]v[i]v[i]

第一維是前 iii 個物品,第二維是背包容量。
dp[i,j]=max?(dp[i][j],dp[i?1][j?k?v[i]]+k?w[i])0≤k≤c[i]dp[i,j]=\max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i])\quad 0\le k\le c[i] dp[i,j]=max(dp[i][j],dp[i?1][j?k?v[i]]+k?w[i])0kc[i]

二進制優化

因為每個數都可以被表示成二進制形式。

二進制優化就是將物品的個數 c[i]c[i]c[i] 拆分成二進制形式,將多個物品捆綁成 2i2^i2i 個形式。

然后通過捆綁后的多重組合,能夠組合出原來 0~c[i]0\sim c[i]0c[i] 的所有選擇。

i.e. 物品個數為 121212,就拆分成 1(20)+2(21)+4(22)+51(2^0)+2(2^1)+4(2^2)+51(20)+2(21)+4(22)+5,因為剩下的不足捆綁,就單獨拎出來處理。

會發現這四個數就能組合出選擇 0~120\sim 12012 個物品的情況。

這就將第三維度枚舉放的物品個數從 O(c[i])O(c[i])O(c[i]) 降到了 O(log?c[i])O(\log c[i])O(logc[i])

如果將所有信息看成同階,則復雜度為 O(n2log?n)O(n^2\log n)O(n2logn)

#include <cstdio> #include <cstring> #define maxn 105 #define maxm 100005 int f[maxm], a[maxn], c[maxn]; int n, m;int main() {while( scanf( "%d %d", &n, &m ) ) {if( ! n and ! m ) break;for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );for( int i = 1;i <= n;i ++ ) scanf( "%d", &c[i] );memset( f, 0, sizeof( f ) );f[0] = 1;for( int i = 1;i <= n;i ++ ) {int k = 1;while( c[i] >= k ) {c[i] -= k;for( int j = m;j >= a[i] * k;j -- )f[j] |= f[j - a[i] * k];k <<= 1;}if( c[i] ) {for( int j = m;j >= a[i] * c[i];j -- )f[j] |= f[j - a[i] * c[i]];}}int ans = 0;for( int i = 1;i <= m;i ++ ) ans += f[i];printf( "%d\n", ans );}return 0; }

單調隊列優化

因為某種物品盡管有很多個,但是體積是一樣的。

每次都會占用背包的 v[i]v[i]v[i] 體積。

所以如果原來背包的容量為 jjj,那么只會轉移給 j+v[i],j+v[i]?2,...,j+v[i]?c[i]j+v[i],j+v[i]*2,...,j+v[i]*c[i]j+v[i],j+v[i]?2,...,j+v[i]?c[i],這些容量在 %v[i]\% v[i]%v[i] 下同余。

將背包容量 jjj,按照 %v[i]\% v[i]%v[i] 的余數分類考慮,顯然兩個不同的余數是不會相互轉移的。

式子化地,令 a=j/v[i],b=j%v[i]?j=a?v[i]+ba=j/v[i],b=j\%v[i]\Rightarrow j=a*v[i]+ba=j/v[i],b=j%v[i]?j=a?v[i]+b

f[i][j]=max?{f[i?1][j?k?v[i]]+k?w[i]}?f[i][j]=max?{f[i?1][(a?k)?v[i]+b]+k?w[i]}f[i][j] = \max\Big\{f[i-1][j-k*v[i]]+k*w[i]\Big\}\Rightarrow f[i][j]=\max\Big\{f[i-1][(a-k)*v[i]+b]+k*w[i]\Big\}f[i][j]=max{f[i?1][j?k?v[i]]+k?w[i]}?f[i][j]=max{f[i?1][(a?k)?v[i]+b]+k?w[i]}

k(0≤k≤c[i])k\ (0\le k\le c[i])k?(0kc[i]) 反正都是枚舉的變量,不妨令 k=a?kk=a-kk=a?k

f[i][j]=max?{f[i?1][k?v[i]+b]+(a?k)?w[i]}f[i][j]=\max\Big\{f[i-1][k*v[i]+b]+(a-k)*w[i]\Big\}f[i][j]=max{f[i?1][k?v[i]+b]+(a?k)?w[i]}

整理得,f[i][j]=max?{f[i?1][k?v[i]+b]?k?w[i]}+a?w[i](a?c[i]≤k≤a)f[i][j]=\max\Big\{f[i-1][k*v[i]+b]-k*w[i]\Big\}+a*w[i]\quad (a-c[i]\le k\le a)f[i][j]=max{f[i?1][k?v[i]+b]?k?w[i]}+a?w[i](a?c[i]ka)

換種形式表達 kkk 的范圍:j/v[i]?c[i]]≤k≤j/v[i]j/v[i]-c[i]]\le k\le j/v[i]j/v[i]?c[i]]kj/v[i]

是關于 jjj 的不減函數,一段區間,所以可以用單調隊列優化。

時間復雜度是 O(nV)O(nV)O(nV),就沒有 logloglog 了。

#include <cstdio> #include <cstring> #define maxn 100005 int n, m, head, tail; int f[maxn], a[maxn], c[maxn], q[maxn];int main() {while( ~ scanf( "%d %d", &n, &m ) ) {if( ! n and ! m ) break;for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );for( int i = 1;i <= n;i ++ ) scanf( "%d", &c[i] );memset( f, 0, sizeof( f ) ); f[0] = 1;for( int i = 1;i <= n;i ++ ) {if( c[i] == 1 ) {for( int j = m;j >= a[i];j -- ) f[j] |= f[j - a[i]];continue;}if( a[i] * c[i] >= m ) {for( int j = a[i];j <= m;j ++ ) f[j] |= f[j - a[i]];continue;}for( int j = 0;j < a[i];j ++ ) {head = 1, tail = 0;for( int k = j;k <= m;k += a[i] ) {while( head <= tail and q[head] < k - a[i] * c[i] ) head ++;if( ! f[k] ) f[k] |= ( head <= tail );else q[++ tail] = k;}}}int ans = 0;for( int i = 1;i <= m;i ++ ) ans += f[i];printf( "%d\n", ans );}return 0; }

總結

以上是生活随笔為你收集整理的【学习笔记】多重背包相关优化——二进制优化/单调队列优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。