背包问题专栏(01,完全,多重)
本題的模板是套用了
A.S.KirigiriKyouko
的模板。請(qǐng)dalao見(jiàn)諒
一、01背包
有N件物品和一個(gè)容量為V的背包。第i件物品的價(jià)格(即體積,下同)是w[i],價(jià)值是c[i]。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過(guò)背包容量,且價(jià)值總和最大。
這是最基礎(chǔ)的背包問(wèn)題,總的來(lái)說(shuō)就是:選還是不選,這是個(gè)問(wèn)題<( ̄ˇ ̄)/
相當(dāng)于用f[i][j]表示前i個(gè)背包裝入容量為v的背包中所可以獲得的最大價(jià)值。
對(duì)于一個(gè)物品,只有兩種情況
情況一: 第i件不放進(jìn)去,這時(shí)所得價(jià)值為:f[i-1][v]
情況二: 第i件放進(jìn)去,這時(shí)所得價(jià)值為:f[i-1][v-c[i]]+w[i]?
狀態(tài)轉(zhuǎn)移方程為:f[i][v] = max(f[i-1][v], f[i-1][v-w[i]]+c[i])
一道裸01背包題↓_↓
采藥
題目描述?Description辰辰是個(gè)天資聰穎的孩子,他的夢(mèng)想是成為世界上最偉大的醫(yī)師。為此,他想拜附近最有威望的醫(yī)師為師。醫(yī)師為了判斷他的資質(zhì),給他出了一個(gè)難題。醫(yī)師把他帶到一個(gè)到處都是草藥的山洞里對(duì)他說(shuō):“孩子,這個(gè)山洞里有一些不同的草藥,采每一株都需要一些時(shí)間,每一株也有它自身的價(jià)值。我會(huì)給你一段時(shí)間,在這段時(shí)間里,你可以采到一些草藥。如果你是一個(gè)聰明的孩子,你應(yīng)該可以讓采到的草藥的總價(jià)值最大。”?
如果你是辰辰,你能完成這個(gè)任務(wù)嗎?
輸入第一行有兩個(gè)整數(shù)T(1<=T<=1000)和M(1<=M<=100),用一個(gè)空格隔開(kāi),T代表總共能夠用來(lái)采藥的時(shí)間,M代表山洞里的草藥的數(shù)目。接下來(lái)的M行每行包括兩個(gè)在1到100之間(包括1和100)的整數(shù),分別表示采摘某株草藥的時(shí)間和這株草藥的價(jià)值。
輸出描述?Output Description輸出包括一行,這一行只包含一個(gè)整數(shù),表示在規(guī)定的時(shí)間內(nèi),可以采到的草藥的最大總價(jià)值。
樣例輸入?Sample Input70?3
71?100
69?1
1?2
3
數(shù)據(jù)范圍及提示?Data Size & Hint【數(shù)據(jù)規(guī)模】
對(duì)于30%的數(shù)據(jù),M<=10;
對(duì)于全部的數(shù)據(jù),M<=100。
再上一維的
#include<bits/stdc++.h> using namespace std; const int maxm = 2001, maxn = 101; int m, n; int w[maxn], c[maxn]; int f[maxm]; int main(){scanf("%d%d",&m, &n); //背包容量m和物品數(shù)量nfor (int i=1; i <= n; i++)scanf("%d%d",&w[i],&c[i]); //每個(gè)物品的重量和價(jià)值for (int i=1; i <= n; i++) //設(shè)f(v)表示重量不超過(guò)v公斤的最大價(jià)值for (int v = m; v >= w[i]; v--) //注意是逆序f[v] = max(f[v-w[i]]+c[i], f[v]);printf("%d\n",f[m]); // f(m)為最優(yōu)解return 0; }二、完全背包
有N種物品和一個(gè)容量為V的背包,每種物品都有無(wú)限件可用。第i種物品的費(fèi)用是w[i],價(jià)值是c[i]。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過(guò)背包容量,且價(jià)值總和最大。
完全背包和01背包十分相像, 區(qū)別就是完全背包物品有無(wú)限件。由之前的選或者不選轉(zhuǎn)變成了選或者不選,選幾件。√
和01背包一樣,我們可以寫(xiě)出狀態(tài)轉(zhuǎn)移方程:f[i][v]=max(f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v)
還有一個(gè)簡(jiǎn)單的優(yōu)化↓_↓
當(dāng)一個(gè)物品的價(jià)值小于另一個(gè)物品的價(jià)值,但是價(jià)格高于另一個(gè)物品,我們就可以不去考慮這個(gè)物品。即若兩件物品i、j滿足c[i]<=c[j]且w[i]>=w[j],則將物品j去掉,不用考慮。我們?yōu)槭裁匆I一個(gè)又貴又難吃的東西呢(╯▽╰)
#include <iostream> #define V 500 using namespace std; int weight[20 + 1]; int value[20 + 1]; int f[V + 1]; int max(int a, int b) {return a > b ? a : b; } int main() {int n, m;cin >> n; for (int i = 1; i <= n; i++) {cin >> weight[i] >> value[i];}cin >> m;for (int i = 1; i <= n; i++) {for (int j = weight[i]; j <= m; j++) {f[j] = max(f[j], f[j - weight[i]] + value[i]);}}cout<< f[m] << endl; }三、多重背包
有N種物品和一個(gè)容量為V的背包。第i種物品最多有n[i]件可用,每件費(fèi)用是w[i],價(jià)值是c[i]。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過(guò)背包容量,且價(jià)值總和最大。
這里又多了一個(gè)限制條件,每個(gè)物品規(guī)定了可用的次數(shù)。
同理,我們可以得出狀態(tài)轉(zhuǎn)移方程:f[i][v]=max(f[i-1][v-k*w[i]]+ k*c[i]|0<=k<=n[i])
一道例題↓_↓
慶功會(huì)
【問(wèn)題描述】
為了慶賀班級(jí)在校運(yùn)動(dòng)會(huì)上取得全校第一名成績(jī),班主任決定開(kāi)一場(chǎng)慶功會(huì),為此撥款購(gòu)買獎(jiǎng)品犒勞運(yùn)動(dòng)員。期望撥款金額能購(gòu)買最大價(jià)值的獎(jiǎng)品,可以補(bǔ)充他們的精力和體力。
【輸入格式】
第一行二個(gè)數(shù)n(n<=500),m(m<=6000),其中n代表希望購(gòu)買的獎(jiǎng)品的種數(shù),m表示撥款金額。 接下來(lái)n行,每行3個(gè)數(shù),v、w、s,分別表示第I種獎(jiǎng)品的價(jià)格、價(jià)值(價(jià)格與價(jià)值是不同的概念)和購(gòu)買的數(shù)量(買0件到s件均可),其中v<=100,w<=1000,s<=10。
【輸出格式】
第一行:一個(gè)數(shù),表示此次購(gòu)買能獲得的最大的價(jià)值(注意!不是價(jià)格)。
【輸入樣例】
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
【輸出樣例】
1040
#include<stdio.h> #include<algorithm> using namespace std; int v[6002], w[6002], s[6002]; int f[6002]; int n, m; int main() {scanf("%d%d",&n,&m);for (int i = 1; i <= n; i++)scanf("%d%d%d",&v[i],&w[i],&s[i]);for (int i = 1; i <= n; i++)for (int j = m; j >= 0; j--)for (int k = 0; k <= s[i]; k++){if (j-k*v[i]<0) break;f[j] = max(f[j],f[j-k*v[i]]+k*w[i]);}printf("%d\n",f[m]);return 0; } //這是未優(yōu)化的再用二進(jìn)制優(yōu)化轉(zhuǎn)換成01
#include<stdio.h> #include<algorithm> using namespace std; int v[10001],w[10001]; int f[6001]; int n,m,n1; int main() {scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int x,y,s,t=1;scanf("%d%d%d",&x,&y,&s);while (s>=t) {v[++n1]=x*t;w[n1]=y*t;s-=t;t*=2;}v[++n1]=x*s;w[n1]=y*s; //把s以2的指數(shù)分堆:1,2,4,…,2^(k-1),s-2^k+1, } for(int i=1;i<=n1;i++)for(int j=m;j>=v[i];j--)f[j]=max(f[j],f[j-v[i]]+w[i]); printf("%d\n",f[m]);return 0; }//先借用一下dalao的代碼,有空我會(huì)慢慢碼上的
?
轉(zhuǎn)載于:https://www.cnblogs.com/crazily/p/9657725.html
總結(jié)
以上是生活随笔為你收集整理的背包问题专栏(01,完全,多重)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 程序员久坐伤身,站起来,走出去,别回来
- 下一篇: 题解 CF38C 【Blinds】