關於幾個揹包問題(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的揹包。
測試

總的來講:spa

方程之中,如今須要放置的是第i件物品,這件物品的重量是Wi,價值是Pi,所以f[i-1,j]表明的就是不將這件物品放入揹包,而f[i-1],j-Wi]]+Pi則是表明將第i件放入揹包以後的總價值,比較二者的價值,得出最大的價值存入如今的揹包之中。.net

附上南陽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。求解將哪些物品裝入揹包可以使這些物品的體積總和不超過揹包容量,且價值總和最大。本題要求是揹包剛好裝滿揹包時,求出最大價值總和是多少。若是不能剛好裝滿揹包,輸出NOblog

輸入
第一行: 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]遞 推而來。換句話說,這正是爲了保證每件物品只選一次,由於質量在減小不可能再能加入一個和原來質量同樣大的物品,
而如今徹底揹包的特色恰是每種物品可選無限件,因此採用「質量增長」的循環,所以後面可能會繼續加入和原來質量同樣的物品。
相關文章
相關標籤/搜索