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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

背包问题——第一篇

發(fā)布時間:2023/12/4 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 背包问题——第一篇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一,01背包

最簡單也是最經典的背包問題。

首先我們知道背包問題是一種d問題,最重要的就是要去找到他的狀態(tài)轉移方程。而在01背包中轉移方程就比較簡單了,這里用一個二維數組進行標表示。
ans[i][j]=max(ans[i-1][j],ans[i-1][j-v[i]+w[i]);
在這里i表示的是第幾件物品,j表示的是背包當前課裝下的最大體積,
0 1 就是有兩種選擇:
不裝第i件物品,此時的狀態(tài)就是,裝上一件物品的相同體積的狀態(tài)。
裝第i件物品,此時還要考慮當前的背包容量能不能裝得下,如果可以,就裝下,此時的價值變成了,ans[i-1][j-v[i]]+w[i],

因為我們要的是在背包中盡可能的裝入跟多的價值,所以這里就要取一個max值

//沒有任何優(yōu)化的版本。 #include<iostream> #include<algorithm> #include<cstring> #define maxn 1005 int ans[maxn][maxn]; int v[maxn],w[maxn]; using namespace std; int main() {int n,m;while(cin>>n>>m) {memset(ans,0,sizeof(ans));for(int i=1;i<=n;i++)cin>>v[i]>>w[i];ans[0][0]=0;for(int i=1;i<=n;i++) {for(int j=0;j<=m;j++) {ans[i][j]=ans[i-1][j];if(j>=v[i])ans[i][j]=max(ans[i][j],ans[i-1][j-v[i]]+w[i]);}}cout<<ans[n][m]<<endl;}return 0; }

這是沒有任何優(yōu)化的版本,也是最好理解背包問題的狀態(tài)轉移方程的代碼。
接下來我們對空間進行優(yōu)化。

//空間優(yōu)化版本。 #include<iostream> #include<cstring> #define maxn 1005 int ans[maxn]; using namespace std; int main() {int n,m;while(cin>>n>>m) {memset(ans,0,sizeof(ans));while(n--) {int v,w;cin>>v>>w;for(int i=m;i>=v;i--) {//注意在這層循環(huán)一定是從大到小,才能保證物品最多只選用一次,ans[i]=max(ans[i],ans[i-v]+w);}}cout<<ans[m]<<endl;}return 0; }

二、完全背包。


這里我們可以對上面的0 1 背包一樣,對于每種可以當成是0 1背包的特殊情況,只不過每件物品是一樣的。
但是在這里要注意的是,共有n次0 1背包循環(huán),

#include<iostream> #include<cstring> #define maxn 1005 int ans[maxn]; using namespace std; int main() {int n,m;while(cin>>n>>m) {memset(ans,0,sizeof(ans));for(int i=0;i<n;i++) {int v,w;cin>>v>>w;for (int j=v;j<=m;j++)//這里剛好跟上面相反,物品能多次選擇ans[j]=max(ans[j],ans[j-v]+w);}cout<<ans[m]<<endl;}return 0;}

三、多重背包。


這一道題當我們理解了前面兩道題目的是后發(fā)現并不難,只要在0 1背包加一重循環(huán)來枚舉數量。
于是我們有了下面的代碼。

//沒有任何優(yōu)化的版本。 #include<iostream> #include<cstring> #define maxn 1005 int ans[maxn]; using namespace std; int main() {int n,m;while(cin>>n>>m) {memset(ans,0,sizeof(ans));while(n--) {int v,w,s;cin>>v>>w>>s;for(int i=1;i<=s;i++) {for(int j=m;j>=i*v;j--) {ans[j]=max(ans[j],ans[j-i*v]+i*w);}}}cout<<ans[m]<<endl;}return 0; }

但是我們仔細想一想,這里的時間復雜度,nVs 已經到了1e9了,這里顯然會超時,想一想我們有沒有什么辦法來組合每一種物品的數量,
比如說15 我們可以變成,1,2,4,8的組合,而且1~15之間的任意數都可以是這幾個數的組合,于是我們想到了用二進制來拆分優(yōu)化數,這里的s就可以降到log2s了,

//二進制優(yōu)化方法。 #include<iostream> #include<algorithm> #include<cstring> #include<vector> #define maxn 100000 int ans[maxn]; struct each {int v,w; }; using namespace std; int main() {int n,m;vector<each>a;while(cin>>n>>m) {memset(ans,0,sizeof(ans));a.clear();for(int i=1;i<=n;i++) {int v1,w1,s;cin>>v1>>w1>>s;for(int i=1;i<=s;i*=2) {s-=i;a.push_back({i*v1,i*w1});}if(s>0)a.push_back({s*v1,s*w1});}for(int i=0;i<a.size();i++) {for(int j=m;j>=a[i].v;j--)ans[j]=max(ans[j],ans[j-a[i].v]+a[i].w);}cout<<ans[m]<<endl;}return 0; }

當然了對于數據沒有這么大的多重背包還是可以用第一個代碼來寫的
其實這里還有一個單調隊列優(yōu)化的,但是我水平有限就,沒給出代碼,了,有興趣可以自己去找一下代碼,

四、混合背包。


這組樣例的答案是8


其實這里就是前三種問題的匯總,我們可以在輸入的時候進行處理進行分類,下面是匯總了目前我會的上面的幾種方法的最優(yōu)解

//二進制優(yōu)化版本,加多混合背包。 #include<iostream> #include<cstring> #include<vector> #include<algorithm> #define maxn 10005 int ans[maxn]; using namespace std; struct each {int v,w; }; int main() {int n,m;vector<each>a;while(cin>>n>>m) {a.clear();memset(ans,0,sizeof(ans));while(n--) {int v1,w1,s;cin>>v1>>w1>>s;if(s==-1) {for(int j=m;j>=v1;j--)ans[j]=max(ans[j],ans[j-v1]+w1);}else if(s==0) {for(int j=v1;j<=m;j++)ans[j]=max(ans[j],ans[j-v1]+w1);}else {for(int i=1;i<=s;i*=2) {s-=i;a.push_back({i*v1,i*w1});}if(s>0)a.push_back({s*v1,s*w1});} }int longs=a.size();for(int i=0;i<longs;i++)for(int j=m;j>=a[i].v;j--)ans[j]=max(ans[j],ans[j-a[i].v]+a[i].w);cout<<ans[m]<<endl; }return 0; }

五、分組背包。


這里我們又可以吧分組背包看成是多重背包的一個特殊例子,每組個數有限制,但是只能取一個

#include<iostream> #include<cstring> #define maxn 105 int ans[maxn],v[maxn],w[maxn]; using namespace std; int main() {int n,m;while(cin>>n>>m) {memset(ans,0,sizeof(ans));while(n--) {int x;cin>>x;for(int i=0;i<x;i++) cin>>v[i]>>w[i];for(int i=m;i>0;i--)for(int j=0;j<x;j++)if(i>=v[j]) ans[i]=max(ans[i],ans[i-v[j]]+w[j]);}cout<<ans[m]<<endl;}return 0; }

剩下還有四個背包以后再更吧,就先寫到這。

總結

以上是生活随笔為你收集整理的背包问题——第一篇的全部內容,希望文章能夠幫你解決所遇到的問題。

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