個人新學的幾個背包問題,做下記錄總結。(參考博客:http://blog.csdn.net/mu399/article/details/7722810 以及 http://blog.csdn.net/u013174702/article/details/45741395)
(1)01背包:
01背包的狀態轉換方程 f[i,j] = Max{ f[i-1,j-Wi]+Pi, f[i-1,j] }
f[i,j]表示在前i件物品中選擇若干件放在承重為 j 的背包中,可以取得的最大價值。
Pi表示第i件物品的價值
。該方程說白了就是比較放第i個和不放第i個物品兩種決策,哪種決策價值大就選擇哪種 。
我們舉個例子來理解下:
假設f[i-1,j]表示我有一個承重為8的背包,當只有物品b,c,d,e四件可選時,這個背包能裝入的最大價值9,現在有個重量Wi為2 價值為Pi為6
的物品a,考慮是否放入承重為8的背包使其價值最大,f[i-1,j-Wi]代表一個承重為6的背包的最大價值(等於當前背包承重8減去物品a的重量2),
當只有物品b,c,d,e四件可選時,這個背包能裝入的最大價值為8
由於f[i-1][v-Wi]+w[i]= 9 + 6 = 15 大於f[i][v] = 8,所以物品a應該放入承重為8的背包。
總的來說:
方程之中,現在需要放置的是第i件物品,這件物品的重量是Wi,價值是Pi,因此f[i-1,j]代表的就是不將這件物品放入背包,而f[i-1],j-Wi]]+Pi則是代表將第i件放入背包之后的總價值,比較兩者的價值,得出最大的價值存入現在的背包之中。
附上南陽oj上的一個題(49題):
開心的小明
時間限制:
1000 ms | 內存限制:65535 KB
難度:
4
- 描述
- 小明今天很開心,家里購置的新房就要領鑰匙了,新房里有一間他自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說:“你的房間需要購買哪些物品,怎么布置,你說了算,只要不超過N 元錢就行”。今天一早小明就開始做預算,但是他想買的東西太多了,肯定會超過媽媽限定的N 元。於是,他把每件物品規定了一個重要度,分為5 等:用整數1~5 表示,第5 等最重要。他還從因特網上查到了每件物品的價格(都是整數元)。他希望在不超過N 元(可以等於N 元)的前提下,使每件物品的價格與重要度的乘積的總和最大。設第j 件物品的價格為v[j],重要度為w[j],共選中了k 件物品,編號依次為j1...jk,則所求的總和為:v[j1]*w[j1]+..+v[jk]*w[jk]請你幫助金明設計一個滿足要求的購物單.
- 輸入
-
第一行輸入一個整數N(0<N<=101)表示測試數據組數
每組測試數據輸入的第1 行,為兩個正整數,用一個空格隔開:
N m
(其中N(<30000)表示總錢數,m(<25)為希望購買物品的個數。)
從第2 行到第m+1 行,第j 行給出了編號為j-1
的物品的基本數據,每行有2 個非負整數
v p
(其中v 表示該物品的價格(v≤10000),p 表示該物品的重要度(1~5)) - 輸出
-
每組測試數據輸出只有一個正整數,為不超過總錢數的物品的價格與重要度乘積的總和的
最大值(<100000000) - 樣例輸入
-
1 1000 5 800 2 400 5 300 5 400 3 200 2
- 樣例輸出
-
3900
解決代碼:
//01背包問題,開心的小明
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int dp[30001];//dp[i]表示質量為i時的最大價值
struct bag{
int v;
int p;
}a[26];
int main(){
int n;
scanf("%d",&n);
while(n--){
int N,m,i,j;
scanf("%d%d",&N,&m);
for(i=1;i<=m;i++)
scanf("%d%d",&a[i].v,&a[i].p);
memset(dp,0,sizeof(dp));
for(i=1;i<=m;i++){
for(j=N;j>=a[i].v;j--){
dp[j]=max(dp[j-a[i].v]+a[i].v*a[i].p,dp[j]);
}//確定要不要買價格為j的第i件物品,總是使dp的值最大
}
printf("%d\n",dp[N]);
}
}
(2)又見01背包:又見01背包(通過南陽Oj一個例題來體現)
時間限制:1000 ms | 內存限制:65535 KB難度:3- 描述
-
有n個重量和價值分別為wi 和 vi 的 物品,從這些物品中選擇總重量不超過 W的物品,求所有挑選方案中物品價值總和的最大值。1 <= n <=1001 <= wi <= 10^71 <= vi <= 1001 <= W <= 10^9
- 輸入
-
多組測試數據。
每組測試數據第一行輸入,n 和 W ,接下來有n行,每行輸入兩個數,代表第i個物品的wi 和 vi。 - 輸出
- 滿足題意的最大價值,每組測試數據占一行。
- 樣例輸入
-
4 5 2 3 1 2 3 4 2 2 - 樣例輸出
-
7
這題起初看以為就是普通的背包問題,仔細一看如果用dp[W]來表示質量為wi時的最大價值,因為W的范圍太大開不了那么大的數組,
所以解決方法就是把價值和重量翻轉,改用較小的價值來開數組,那么最后求的就是指定價值下的最小重量。
附上代碼:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct bag{
int w;
int v;
}a[101];
int dp[100000];
int main(){
int n,W;
while(scanf("%d%d",&n,&W)!=EOF){
int i,j,sum=0;
for(i=0;i<n;i++){
scanf("%d%d",&a[i].w,&a[i].v);
sum+=a[i].v;
}
for(i=0;i<=sum;i++)
dp[i]=1e9;
dp[0]=0;
for(i=0;i<n;i++){
for(j=sum;j>=a[i].v;j--){
dp[j]=min(dp[j-a[i].v]+a[i].w,dp[j]);//dp[]代表指定價值下的最小重量,j為指定價值
}
}
for(i=sum;i>=0;i--){//按順序從大到小輸出dp的值,即重量對應的價值
if(dp[i]<=W){
printf("%d\n",i);
break;
}
}
}
}
(3)完全背包:完全背包(南陽oj311題)
時間限制: 3000 ms | 內存限制:65535 KB難度: 4- 描述
-
直接說題意,完全背包定義有N種物品和一個容量為V的背包,每種物品都有無限件可用。第i種物品的體積是c,價值是w。求解將哪些物品裝入背包可使這些物品的體積總和不超過背包容量,且價值總和最大。本題要求是背包恰好裝滿背包時,求出最大價值總和是多少。如果不能恰好裝滿背包,輸出NO
- 輸入
-
第一行: N 表示有多少組測試數據(N<7)。
接下來每組測試數據的第一行有兩個整數M,V。 M表示物品種類的數目,V表示背包的總容量。(0<M<=2000,0<V<=50000)
接下來的M行每行有兩個整數c,w分別表示每種物品的重量和價值(0<c<100000,0<w<100000) - 輸出
- 對應每組測試數據輸出結果(如果能恰好裝滿背包,輸出裝滿背包時背包內物品的最大價值總和。 如果不能恰好裝滿背包,輸出NO)
- 樣例輸入
-
2 1 5 2 2 2 5 2 2 5 1
代碼:
#include<stdio.h>
#include<algorithm>
using namespace std;
int dp[50005],c[2001],w[2001];
int main(){
int N,M,V,i,j;
scanf("%d",&N);
while(N--){
scanf("%d%d",&M,&V);
for(i=1;i<=V;i++)
dp[i]=-1000000;
dp[0]=0;
for(i=0;i<M;i++)
scanf("%d%d",&c[i],&w[i]);
for(i=0;i<M;i++){
for(j=c[i];j<=V;j++){
dp[j]=max(dp[j-c[i]]+w[i],dp[j]);
}
}
if(dp[V]<0)
printf("NO\n");
else
printf("%d\n",dp[V]);
}
}
解題思路:0-1背包的狀態轉移方程是
for i = 1 to N for v = V to Ci F [v] = max{F [v],F [v − Ci] + Wi}
完全背包就是不限制物品使用個數,可以無限使用,也就是可以重復放置一個物體轉移方程for i = 1 to N for v = Ci to V F [v] = max(F [v], F [v − Ci] + Wi)
你會發現,這個偽代碼與01背包問題的偽代碼只有v的循環次序不同而已。 為什么這個算法就可行呢?首先想想為什么01背包中要按照v遞減的次序來 循環。讓v遞減是為了保證第i次循環中的狀態F [i, v]是由狀態F [i − 1, v − Ci]遞 推而來。換句話說,這正是為了保證每件物品只選一次,因為質量在減少不可能再能加入一個和原來質量一樣大的物品,
而現在完全背包的特點恰是每種物品可選無限件,所以采用“質量增加”的循環,因此后面可能會繼續加入和原來質量一樣的物品。。
