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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

经典背包问题 01背包+完全背包+多重背包

發(fā)布時(shí)間:2025/3/16 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 经典背包问题 01背包+完全背包+多重背包 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

01 背包

有n 種不同的物品,每個(gè)物品有兩個(gè)屬性,size 體積,value 價(jià)值,現(xiàn)在給一個(gè)容量為 w 的背包,問最多可帶走多少價(jià)值的物品。 ?

int f[w+1]; //f[x] 表示背包容量為x 時(shí)的最大價(jià)值 for (int i=0; i<n; i++)for (int j=w; j>=size[i]; j--)f[j] = max(f[j], f[j-size[i]]+value[i]);



完全背包?

如果物品不計(jì)件數(shù),就是每個(gè)物品不只一件的話,稍微改下即可 ?

for (int i=0; i<n; i++)for (int j=size[i]; j<=w; j++)f[j] = max(f[j], f[j-size[i]]+value[i]);

??
? ? ? ? ?f[w] 即為所求 ?
??????? 初始化分兩種情況:
??????? 1、如果背包要求正好裝滿則初始化 f[0] = 0, f[1~w] = -INF; ?

??????? 2、如果不需要正好裝滿 f[0~v] = 0; ?

? ? ? ? 舉例:

01背包

V=10,N=3,c[]={3,4,5}, w={4,5,6}

(1)背包不一定裝滿

? ? ? 計(jì)算順序是:從右往左,自上而下:因?yàn)槊總€(gè)物品只能放一次,前面的體積小的會(huì)影響體積大的

(2)背包剛好裝滿 ? ?

? ? ? 計(jì)算順序是:從右往左,自上而下。注意初始值,其中-inf表示負(fù)無窮

完全背包

V=10,N=3,c[]={3,4,5}, w={4,5,6}

(1)背包不一定裝滿

計(jì)算順序是:從左往右,自上而下: ?每個(gè)物品可以放多次,前面的會(huì)影響后面的

(2)背包剛好裝滿

計(jì)算順序是:從左往右,自上而下。注意初始值,其中-inf表示負(fù)無窮

多重背包 : ?
? ? ? ? ?多重背包問題要求很簡單,就是每件物品給出確定的件數(shù),求 可得到的最大價(jià)值 ?
? ? ? ? ?多重背包轉(zhuǎn)換成 01 背包問題就是多了個(gè)初始化,把它的件數(shù)C 用二進(jìn)制 分解成若干個(gè)件數(shù)的集合,這里面數(shù)字可以組合成任意小于等于C 的件數(shù),而且不會(huì)重復(fù),之所以叫二進(jìn)制分解,是因?yàn)檫@樣分解可 以用數(shù)字的二進(jìn)制形式來解釋 ?
? ? ? ?比如:7的二進(jìn)制 7 = 111 它可以分解成 001 010 100 這三個(gè)數(shù)可以 組合成任意小于等于7 的數(shù),而且每種組合都會(huì)得到不同的數(shù) ?
? ? ? ?15 = 1111 可分解成 0001? 0010? 0100? 1000 四個(gè)數(shù)字 ?
??????? 如果13 = 1101 則分解為 0001 0010 0100 0110 前三個(gè)數(shù)字可以組合成 ? 7以內(nèi)任意一個(gè)數(shù),即1、2、4可以組合為1——7內(nèi)所有的數(shù),加上 0110 = 6 可以組合成任意一個(gè)大于6 小于等于13 的數(shù),比如12,可以讓前面貢獻(xiàn)6且后面也貢獻(xiàn)6就行了。雖然有重復(fù)但總是能把 13 以內(nèi)所有的數(shù)都考慮到了,基于這種 思想去把多件物品轉(zhuǎn)換為,多種一件物品,就可用01 背包求解了。 ?
???? ? 看代碼: ?
?????? int n; //輸入有多少種物品 int c; //每種物品有多少件 int v; //每種物品的價(jià)值 int s; //每種物品的尺寸 int count = 0; //分解后可得到多少種物品 int value[MAX]; //用來保存分解后的物品價(jià)值 int size[MAX]; //用來保存分解后物品體積scanf("%d", &n); //先輸入有多少種物品,接下來對(duì)每種物品進(jìn)行分解while (n--) //接下來輸入n中這個(gè)物品 {scanf("%d%d%d", &c, &s, &v); //輸入每種物品的數(shù)目和價(jià)值for (int k=1; k<=c; k<<=1) //<<右移 相當(dāng)于乘二{value[count] = k*v;size[count++] = k*s;c -= k;}if (c > 0){value[count] = c*v;size[count++] = c*s;} }

定理:一個(gè)正整數(shù)n可以被分解成1,2,4,…,2^(k-1),n-2^k+1(k是滿足n-2^k+1>0的最大整數(shù))的形式,且1~n之內(nèi)的所有整數(shù)均可以唯一表示成1,2,4,…,2^(k-1),n-2^k+1中某幾個(gè)數(shù)的和的形式。

證明如下:

(1) 數(shù)列1,2,4,…,2^(k-1),n-2^k+1中所有元素的和為n,所以若干元素的和的范圍為:[1, n];

(2)如果正整數(shù)t<= 2^k – 1,則t一定能用1,2,4,…,2^(k-1)中某幾個(gè)數(shù)的和表示,這個(gè)很容易證明:我們把t的二進(jìn)制表示寫出來,很明顯,t可以表示成n=a0*2^0+a1*2^1+…+ak*2^(k-1),其中ak=0或者1,表示t的第ak位二進(jìn)制數(shù)為0或者1.

(3)如果t>=2^k,設(shè)s=n-2^k+1,則t-s<=2^k-1,因而t-s可以表示成1,2,4,…,2^(k-1)中某幾個(gè)數(shù)的和的形式,進(jìn)而t可以表示成1,2,4,…,2^(k-1),s中某幾個(gè)數(shù)的和(加數(shù)中一定含有s)的形式。

(證畢!)


???? ?

??????? 現(xiàn)在用count 代替 n 就和01 背包問題完全一樣了??

杭電2191題解:此為多重背包用01和完全背包:

#include<stdio.h> #include<string.h> int dp[102]; int p[102],h[102],c[102]; int n,m; void comback(int v,int w)//經(jīng)費(fèi),重量。完全背包; {for(int i=v; i<=n; i++)if(dp[i]<dp[i-v]+w)dp[i]=dp[i-v]+w; } void oneback(int v,int w)//經(jīng)費(fèi),重量;01背包; {for(int i=n; i>=v; i--)if(dp[i]<dp[i-v]+w)dp[i]=dp[i-v]+w; } int main() {int ncase,i,j,k;scanf("%d",&ncase);while(ncase--){memset(dp,0,sizeof(dp));scanf("%d%d",&n,&m);//經(jīng)費(fèi),種類;for(i=1; i<=m; i++){scanf("%d%d%d",&p[i],&h[i],&c[i]);//價(jià)值,重量,數(shù)量;if(p[i]*c[i]>=n) comback(p[i],h[i]);else{for(j=1; j<c[i]; j<<1){oneback(j*p[i],j*h[i]);c[i]=c[i]-j;}oneback(p[i]*c[i],h[i]*c[i]);}}printf("%d\n",dp[n]);}return 0; }

只是用01背包,用二進(jìn)制優(yōu)化:

#include <iostream> using namespace std; int main() {int nCase,Limit,nKind,i,j,k, v[111],w[111],c[111],dp[111];//v[]存價(jià)值,w[]存尺寸,c[]存件數(shù)//在本題中,價(jià)值是米的重量,尺寸是米的價(jià)格int count,Value[1111],size[1111];//count存儲(chǔ)分解完后的物品總數(shù)//Value存儲(chǔ)分解完后每件物品的價(jià)值//size存儲(chǔ)分解完后每件物品的尺寸cin>>nCase;while(nCase--){count=0;cin>>Limit>>nKind;for(i=0; i<nKind; i++){cin>>w[i]>>v[i]>>c[i];//對(duì)該種類的c[i]件物品進(jìn)行二進(jìn)制分解for(j=1; j<=c[i]; j<<=1){//<<左移1位,相當(dāng)于乘2Value[count]=j*v[i];size[count++]=j*w[i];c[i]-=j;}if(c[i]>0){Value[count]=c[i]*v[i];size[count++]=c[i]*w[i];}}//經(jīng)過上面對(duì)每一種物品的分解,//現(xiàn)在Value[]存的就是分解后的物品價(jià)值//size[]存的就是分解后的物品尺寸//count就相當(dāng)于原來的n//下面就直接用01背包算法來解memset(dp,0,sizeof(dp));for(i=0; i<count; i++)for(j=Limit; j>=size[i]; j--)if(dp[j]<dp[j-size[i]]+Value[i])dp[j]=dp[j-size[i]]+Value[i];cout<<dp[Limit]<<endl;}return 0; }

未優(yōu)化的:

#include<iostream> #include<cstdio> #include<cstring> using namespace std;int Value[105]; int Cost[105]; int Bag[105]; int dp[105];int main() {int C,m,n;scanf("%d",&C);while(C--){scanf("%d%d",&n,&m);for(int i = 1; i <= m; i++)scanf("%d%d%d",&Cost[i],&Value[i],&Bag[i]);memset(dp,0,sizeof(dp));for(int i=1; i<= m; i++)for(int j=1; j<=Bag[i]; j++)for(int k=n; k>=Cost[i]; k--)dp[k]=max(dp[k], dp[k-Cost[i]]+Value[i]);printf("%d\n",dp[n]);}return 0; }

總結(jié)

以上是生活随笔為你收集整理的经典背包问题 01背包+完全背包+多重背包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。