zoukankan      html  css  js  c++  java
  • 背包问题

    背包问题有好多种,那些名称也记得不太清,随便称呼吧,会做题就行,先试试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
    ",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
    ",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
    ",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
    ",j);break;}
    
        }
        return 0;
    }
    
    
    
     
  • 相关阅读:
    luogu_P3195 [HNOI2008]玩具装箱TOY
    CF_837D
    luogu_P3584 [POI2015]LAS
    大一下存活纪实
    Mathematical Logic for Computer Science 读书笔记
    集训补题合集
    图论笔记4 平面图与可平面图
    软件分析笔记3 DFA
    软件分析笔记2 IR
    软件分析笔记1 Intro
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/9502828.html
Copyright © 2011-2022 走看看