關於幾個背包問題(C語言)


個人新學的幾個背包問題,做下記錄總結。(參考博客: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 <=100
  1 <= wi <= 10^7
  1 <= vi <= 100
  1 <= 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]遞 推而來。換句話說,這正是為了保證每件物品只選一次,因為質量在減少不可能再能加入一個和原來質量一樣大的物品,
而現在完全背包的特點恰是每種物品可選無限件,所以采用“質量增加”的循環,因此后面可能會繼續加入和原來質量一樣的物品。



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM