背包問題


背包問題有好多種,那些名稱也記得不太清,隨便稱呼吧,會做題就行,先試試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;
*/
View Code

 

題目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;
}
View Code

題目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;
}
 
           

 

 


免責聲明!

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



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