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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

多重背包的二进制优化(ybtoj-宝物筛选)

發布時間:2023/12/3 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多重背包的二进制优化(ybtoj-宝物筛选) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 題目描述
  • 解析
    • 樸素算法
      • 代碼
    • 二進制優化
      • 代碼
  • thanks for reading!

題目描述

解析

樸素算法

首先考慮樸素算法
把數量為num的物體拆成num個子物體
其價值與重量是原物體的1,2,3…num倍
然后當成獨立的物體求就行了
注意應該先枚舉重量,再枚舉子物體
因為這些子物體是不能同時取的 (因為同時取時,總個數可能會超過num)
時間復雜度:nwm
(這題這做法也能過就離譜)

代碼

#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; const int N=1e6+100; int m,n; int W,f[N]; struct node{int v,w,num; }p[N]; int main(){scanf("%d%d",&n,&W);for(int i=1;i<=n;i++){scanf("%d%d%d",&p[i].v,&p[i].w,&p[i].num);}for(int i=1;i<=n;i++){for(int ww=W;ww>=p[i].w;ww--){for(int k=1;k<=p[i].num;k++){if(p[i].w*k>ww) break;f[ww]=max(f[ww],f[ww-p[i].w*k]+k*p[i].v);}}}int ans=0;for(int i=0;i<=W;i++){ans=max(ans,f[i]);}printf("%d",ans);return 0; } /* 4 20 3 9 3 5 9 1 9 4 2 8 1 3 */

二進制優化

剛才的枚舉拆分顯然十分低效
我們如何才能把物品拆分的效率提高呢?
我們嘗試把每樣物品拆成完全獨立的物品
(也就是說可以同時取
那么我們的拆分應不重不漏,也就是要滿足以下條件:

1.加在一起不能超過總數量
2.能組合表示出1-num的所有數

顯然要考慮二進制
定義sum數組:

sum[k]= 20 + 21 +22+… + 2k

找到一個最大的k,滿足:

sum[k]<=num

再讓:

R=num-sum[k]

這樣我們把物品拆成k+2個
其大小分別為:

20、 21、22、… 2k、R

由于R是減出來的,加起來肯定不會超過num,條件1成立了

第二個條件,能表示出1-num的所有數的證明,可以分成兩部分:
1.對于<=sum[k]的數,顯然可以用2的0-k次冪用二進制拆分表示出來

2.對與>2k的數A,把它減去R,也就是先拆出來一個R,由R的定義可得:

A-R <= num-R = sum[k]

這樣減去后又是一個<=sum[k]的數,就轉化為情況1了
條件二證畢
(具體的代碼實現中,我預處理了一個數組q[i]存儲num為i時符合條件的k值)
時間復雜度:nwlog(m)

代碼

#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; const int N=1e6+100; int m,n; ll W,f[N]; struct node{int v,w,num; }p[N]; int mi[50],q[N],sum[50]; void solve(){mi[0]=1;for(int i=1;i<=30;i++) mi[i]=mi[i-1]*2;sum[0]=1;q[1]=q[2]=0;int res=1,now=3;for(int k=1;k<=18;k++){res+=mi[k];for(int i=now;i<res;i++) q[i]=k-1;now=res;sum[k]=res;} } int main(){scanf("%d%d",&n,&W);for(int i=1;i<=n;i++){scanf("%d%d%d",&p[i].v,&p[i].w,&p[i].num);}solve();for(int i=1;i<=n;i++){int k=q[p[i].num];for(int j=0;j<=k;j++){ll nw=p[i].w*mi[j],nv=p[i].v*mi[j];for(int p=W;p>=nw;p--){f[p]=max(f[p],f[p-nw]+nv);}}int r=p[i].num-sum[k];ll nv=p[i].v*r,nw=p[i].w*r;for(int p=W;p>=nw;p--){f[p]=max(f[p],f[p-nw]+nv);}}ll ans=0;for(int i=0;i<=W;i++){ans=max(ans,f[i]);}printf("%lld",ans);return 0; } /* 4 20 3 9 3 5 9 1 9 4 2 8 1 3 */

thanks for reading!

總結

以上是生活随笔為你收集整理的多重背包的二进制优化(ybtoj-宝物筛选)的全部內容,希望文章能夠幫你解決所遇到的問題。

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