我的新學的幾個揹包問題,作下記錄總結。(參考博客:http://blog.csdn.net/mu399/article/details/7722810 以及 http://blog.csdn.net/u013174702/article/details/45741395)算法
(1)01揹包:數組
假設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題):設計
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揹包:
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)徹底揹包:
直接說題意,徹底揹包定義有N種物品和一個容量爲V的揹包,每種物品都有無限件可用。第i種物品的體積是c,價值是w。求解將哪些物品裝入揹包可以使這些物品的體積總和不超過揹包容量,且價值總和最大。本題要求是揹包剛好裝滿揹包時,求出最大價值總和是多少。若是不能剛好裝滿揹包,輸出NOblog
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]遞 推而來。換句話說,這正是爲了保證每件物品只選一次,由於質量在減小不可能再能加入一個和原來質量同樣大的物品,
而如今徹底揹包的特色恰是每種物品可選無限件,因此採用「質量增長」的循環,所以後面可能會繼續加入和原來質量同樣的物品。。