背包問題有好多種,那些名稱也記得不太清,隨便稱呼吧,會做題就行,先試試4種常見的背包問題。
題目1:普通的背包問題(0-1背包)
有 n 個重量和價值分別為Wi,Vi的物品,現從這些物品中挑選出總量不超過 W 的物品,求所有方案中價值總和的最大值。
Input:
輸入包含多組測試用例,每一例的開頭為兩位整數 n、W(1<=n<=10000,1<=W<=1000) ,接下來有 n 行,每一行有兩位整數 Wi、Vi(1<=Wi<=10000,1<=Vi<=100)。
Output:
輸出為一行,即所有方案中價值總和的最大值。
Sample Input:
3 4 1 2 2 5 3 7
Sample Output:
9
思路:相同容量找最大值,取和不取找最大值。
AC代碼:
#include<stdio.h> #include<algorithm> #include<iostream> #include<cstring> using namespace std; int dp[10050]; int wi[10050],vi[10050]; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { memset(dp,0,sizeof(dp)); ///重量,價格 for(int i=0;i<n;i++) scanf("%d%d",&wi[i],&vi[i]); for(int i=0;i<n;i++) for(int j=m;j>=wi[i];j--) dp[j]= max( dp[j], dp[ j-wi[i]]+vi[i]); printf("%d\n",dp[m]); } return 0; }
模擬過程:

/* 5個物品,背包容量10 物品下標 重量w 價格v 0 3 4 1 4 5 2 7 3 3 2 6 4 1 1 dp[0] dp[1] dp[2] dp[3] dp[4] dp[5] dp[6] dp[7] dp[8] dp[9] dp[10] dp[11] 輸入n=5,W=10 for(i=0;i<n;i++) i=0;i<5; /// 不放 放 取最大值 j=10; j>=w[0]=3; dp[10]=max( dp[10]=0; dp[10-3]+4)=4 j=9; j>=w[0]=3; dp[9]= max( dp[9]=0; dp[9-3]+4)=4 j=8; j>=w[0]=3; dp[8]= max( dp[8]=0; dp[8-3]+4)=4 j=7; j>=w[0]=3; dp[7]= max( dp[7]=0; dp[7-3]+4)=4 j=6; j>=w[0]=3; dp[6]= max( dp[6]=0; dp[6-3]+4)=4 j=5; j>=w[0]=3; dp[5]= max( dp[5]=0; dp[5-3]+4)=4 j=4; j>=w[0]=3; dp[4]= max( dp[4]=0; dp[4-3]+4)=4 j=3; j>=w[0]=3; dp[3]= max( dp[3]=0; dp[3-3]+4)=4 i=1;i<5; j=10; j>=w[1]=4; dp[10]=max( dp[10]=4; dp[10-4]=4+5)=9///給出10容量,不放和放取最大值 j=9; j>=w[1]=4; dp[9]=max ( dp[9]=4; dp[9-4]=4+5)=9 j=8; j>=w[1]=4; dp[8]=max ( dp[8]=4; dp[8-4]=4+5)=9 j=7; j>=w[1]=4; dp[7]=max ( dp[7]=4; dp[7-4]=4+5)=9 j=6; j>=w[1]=4; dp[6]=max ( dp[6]=4; dp[6-4]=0+5)=5///給出6容量,第一第二物品二選一,價格取最大值 j=5; j>=w[1]=4; dp[5]=max ( dp[5]=4; dp[5-4]=0+5)=5 j=4; j>=w[1]=4; dp[4]=max ( dp[4]=4; dp[4-4]=0+5)=5 i=2;i<5;///只要放得下,都找到最優解,如果放不下,不會更新記錄 j=10; j>=w[2]=7; dp[10]=max( dp[10]=9; dp[10-7]=4+3)=9///一個是之前的放入12物品的最優解,比放入第三個物品價格還高 j=9; j>=w[2]=7; dp[9]=max ( dp[9]=9; dp[9-7]=0+3)=9///第二個物品7的重量,給出9容量的背包,只能放下第二個物品,價格明顯不划算 j=8; j>=w[2]=7; dp[8]=max ( dp[8]=9; dp[8-7]=0+3)=9 j=7; j>=w[2]=7; dp[7]=max ( dp[7]=9; dp[7-7]=0+3)=9 i=3;i<5; j=10; j>=w[3]=2; dp[10]=max( dp[10]=9; dp[10-2]=9+6)=15///放3個物品價格最高 j=9; j>=w[3]=2; dp[9]= max( dp[9]=9; dp[9-2]=9+6)=15 j=8; j>=w[3]=2; dp[8]= max( dp[8]=9; dp[8-2]=5+6)=11///給出8容量背包,放2、4物品最優 j=7; j>=w[3]=2; dp[7]= max( dp[7]=9; dp[7-2]=5+6)=11 j=6; j>=w[3]=2; dp[6]= max( dp[6]=5; dp[6-2]=5+6)=11 j=5; j>=w[3]=2; dp[5]= max( dp[5]=5; dp[5-2]=4+6)=10///給出5容量背包,放1、4物品最優 j=4; j>=w[3]=2; dp[4]= max( dp[4]=5; dp[4-2]=0+6)=6///以下除了放4物品,其他都放不下 j=3; j>=w[3]=2; dp[4]= max( dp[3]=4; dp[4-2]=0+6)=6 j=2; j>=w[3]=2; dp[4]= max( dp[2]=0; dp[2-2]=0+6)=6 i=4;i<5; j=10; j>=w[4]=1; dp[10]=max( dp[10]=15; dp[10-1]=15+1)=16; j=9; j>=w[4]=1; dp[9]= max( dp[9]=15; dp[9-1]=11+1)=15; j=8; j>=w[4]=1; dp[8]= max( dp[8]=11; dp[8-1]=11+1)=12; j=7; j>=w[4]=1; dp[7]= max( dp[7]=11; dp[7-1]=11+1)=12; j=6; j>=w[4]=1; dp[6]= max( dp[6]=11; dp[6-1]=11+1)=12; j=5; j>=w[4]=1; dp[5]= max( dp[5]=10; dp[5-1]=10+1)=11; j=4; j>=w[4]=1; dp[4]= max( dp[4]=6; dp[4-1]=6+1)=7; j=3; j>=w[4]=1; dp[3]= max( dp[3]=6; dp[3-1]=6+1)=7; j=2; j>=w[4]=1; dp[2]= max( dp[2]=6; dp[2-1]=0+1)=6; j=1; j>=w[4]=1; dp[1]= max( dp[1]=6; dp[1-1]=0+1)=1; */
題目2:總和恰好是背包重量的問題,完全背包?
有 n 個重量和價值分別為Wi,Vi的物品,現從這些物品中挑選出總量剛好為 W 的物品 ,求所有方案中價值總和的最大值。
Input:
輸入包含多組測試用例,每一例的開頭為兩位整數 n、W(1<=n<=10000,1<=W<=1000) ,接下來有 n 行,每一行有兩位整數 Wi、Vi(1<=Wi<=10000,1<=Vi<=100)。
Output:
輸出為一行,即所有方案中價值總和的最大值。若不存在剛好填滿的情況,輸出“-1”。
Sample Input:
3 4 1 2 2 5 2 1 3 4 1 2 2 5 5 1
Sample Output:
6 -1
AC代碼:
#include<stdio.h> #include<algorithm> #include<iostream> #include<cstring> using namespace std; int dp[10050];///下標是背包容量, int wi[10050],vi[10050]; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { memset(dp,-1,sizeof(dp)); dp[0]=0; ///重量,價格 for(int i=0;i<n;i++) scanf("%d%d",&wi[i],&vi[i]); for(int i=0;i<n;i++) for(int j=m;j>=wi[i];j--)///從最大重量開始找 if(dp[ j-wi[i] ]!=-1) ///一開始除了dp[0]外都是-1,如果j-wi[i]=0,證明把背包放滿了, dp[j]= max( dp[j], dp[ j-wi[i]]+vi[i]); printf("%d\n",dp[m]); ///最后直接輸出背包重量為m的情況就行。 } return 0; }
模擬過程:

5個物品,背包容量10 物品下標 重量w 價格v 0 3 4 1 4 5 2 3 3 3 2 6 4 2 4 { memset(dp,-1,sizeof(dp)); for i=0;i<n;i++ for從m到小找到能放滿的情況即j-wi[0]=0的情況 ... dp[3]=max(dp[3]=-1,dp[3-3]+4=0+4=4)=4; //放入3重量后的最大價格 i=1;i<n; 找到j-wi[1]=0或3的情況 ... dp[7]=max(dp[7]=-1,dp[7-4]+5=4+5=9)=9; //放入7重量后的最大價格,目前是第一和第二物品的總價格 ... dp[4]=max(dp[4]=-1,dp[4-4]+5=0+5=5)=5; //放入3重量后的最大價格 i=2;i<n; 找到j-wi[2]=0或3或7或4的情況 dp[10]=max(dp[10]=-1,dp[10-7]+3=9+3=12)=12; ///放入10重量后的最大價格,目前是前三個物品的總價格 ... dp[7]=max(dp[7]=9,dp[7-3]=5+3=8)=9;///放入7重量后的最大價格,第一物品+第二物品vs第一物品+第三物品,明顯前者價格大 dp[6]=max(dp[6]=-1,dp[6-3]=4+3=7)=7;/////放入6重量后的最大價格,目前是第一和第三物品的總價格 ... dp[3]=max(dp[3]=4,dp[3-3]=0+3=3)=4;///放入3重量后的最大價格,第一物品vs第三物品,明顯前者價格大 i=3;i<n; 找到 dp[ j-wi[i] ]!=-1 的情況 ... dp[9]=max(dp[9]=-1,dp[9-2]=9+6=15)=15;///1+2+4 dp[8]=max(dp[8]=-1,dp[8-2]=7+6=13)=13;///1+3+4 ... dp[6]=max(dp[6]=7,dp[6-2]=5+6=11)=11;///1+3 vs 2+4 后者價格大 dp[5]=max(dp[5]=-1,dp[5-2]=4+6=10)=10;///1+4不需要和3+4比較了,之前放入三重量的最優解就是放入第一物品 ... dp[2]=max(dp[2]=-1,dp[2-2]=0+6=6)=6;///4 ... i=4;i<5; 找到 dp[ j-wi[i] ]!=-1 的情況 dp[10]=max(dp[10]=12,dp[10-2]=13+4=17)=17; ///1+2+3 vs 1+3+5 dp[9]=max(dp[9]=15;dp[9-2]=9+4=13)=13; ///1+2+4 vs 1+2+5 dp[8]=max(dp[8]=13;dp[8-2]=11+4=15)=15; ///1+3+4 vs 1+3+5 dp[7]=max(dp[7]=9;dp[7-2]=10+4=14)=14; ///1+3 vs 1+4+5 dp[6]=max(dp[6]=11;dp[6-2]=5+4=9)=11; ///1+3 vs 2+5 dp[5]=max(dp[5]=10;dp[5-2]=4+4=8)=10; ///1+4 vs 1+5 dp[4]=max(dp[4]=5;dp[4-2]=6+4=10)=10; /// 2 vs 4+5 dp[2]=max(dp[2]=6;dp[2-2]=0+4=4)=6; /// 4 vs 5 break; }
題目3:可以取的物品有無限個,無限背包?
有 n 種(每一種有無數個)重量和價值分別為Wi,Vi的物品,現從這些物品中挑選出總 量不超過 W 的物品,求所有方案中價值總和的最大值。
Input:
輸入包含多組測試用例,每一例的開頭為兩位整數 n、W(1<=n<=10000,1<=W<=1000) ,接下來有 n 行,每一行有兩位整數 Wi、Vi(1<=Wi<=10000,1<=Vi<=100)
Output:
輸出為一行,即所有方案中價值總和的最大值。
Sample Input:
3 4 1 2 2 5 3 7 3 5 2 3 3 4 4 5
Sample Output:
10 7
AC代碼:
#include<stdio.h> #include<algorithm> #include<iostream> #include<cstring> using namespace std; int dp[10005]; int w[10005],v[10005];///重量,價格 int main()///1133 { int n,m;///物品數,背包重量 while(scanf("%d%d",&n,&m)!=EOF) { memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) scanf("%d%d",&w[i],&v[i]); for(int i=0;i<n;i++) for(int j=w[i];j<=m;j++)///從物品的大小開始,只要你放得下,隨便放,取最大值 dp[j]=max(dp[j],dp[ j-w[i] ]+v[i] ); printf("%d\n",dp[m]); } return 0; }
題目4:變相普通背包?
有 n 個重量和價值分別為Wi,Vi的物品,現從這些物品中挑選出總量不超過 W 的物品,求所有方案中價值總和的最大值。
Input:
輸入包含多組測試用例,每一例的開頭為兩位整數 n、W;接下來有 n 行,每一行有兩位整數 Wi、Vi 其中: 1<=n<=100 1<=W<=1000,000,000 1<=Wi<=10,000,000 1<=Vi<=100。
Output:
輸出為一行,即所有方案中價值總和的最大值。
Sample Input:
4 5 2 3 1 2 3 4 2 2 4 10000000 2 3 2 2 3 3 1 2
Sample Output:
7 10
思路:用第一種方法明顯爆時間,我們可以看到物品價格是比較小的,轉換一下思路,相同的價格取總重量最小的物品
AC代碼:
#include<iostream> #include<cstring> #include<stdio.h> #include<bits/stdc++.h> using namespace std; const int inf=1111111111; ///dp下標存放價格,內容存放重量 int dp[10005]; int main() { int n,m;/// while( scanf("%d%d", &n,&m)!=EOF) { for(int i=0;i<10005;i++) dp[i]=inf;///重量巨大,取重量小的,自然就把這些篩掉 dp[0]=0; int w[n],v[n]; for(int i=0;i<n;i++) scanf("%d%d",&w[i],&v[i]);///重量,價格 for(int i=0;i<n;i++) for(int j=10004;j>=v[i];j--)///最高價格 = 數量*物品單價 再多一些 if( dp[ j-v[i] ]!=inf && dp[ j-v[i] ]+w[i]<=m ) ///重量從0開始取,並且放下這個物品后 重量比背包重量小 dp[j] = min( dp[j], dp[ j-v[i] ]+w[i] ); ///相同的價格,取重量最小 for(int j=10004; j>=0; j--) if( dp[j]!=inf ) ///只要重量不是無窮,那么就是有東西的,第一個撞見的就是價格最大的 {printf("%d\n",j);break;} } return 0; }