• 基础背包问题


    01背包

    1:01背包
    有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

    第 i 件物品的体积是 vi,价值是 wi。

    求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
    输出最大价值。

    输入格式
    第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

    接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

    输出格式
    输出一个整数,表示最大价值。

    数据范围
    0<N,V≤1000
    0<vi,wi≤1000
    输入样例
    4 5
    1 2
    2 4
    3 4
    4 5
    输出样例:
    8

    思路
    题意为在n件物品中挑选体积不超过m的物品,使之总价值最大。我们令dp[i][j]表示有i件物品体积不超过j时的最大价值。那么对于每件物品 ,我们有选或者不选两种状态 。每次我们取最大价值的拿法,最终输出dp[n][m]即可。状态转移方程为dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w) 。这里需要注意的是 ,动态规划中后面的每一项 ,都是由前一项转移而来 。我们可以对其空间进行优化 ,将二维数组改为一维数组 ,以dp[j]表示有i件物品体积不超过j时的最大价值 。此时在枚举体积时,我们需要从大到小枚举。因为我们在求i件物品时 ,需要的利用是i-1件物品时的数据。假如我们从小到大枚举 ,那么在求第i件物品 j体积时小于j的数值已经被我们更新为第i件时的数据了,导致我们无法进行后面的计算 。还需要注意的一个细节是 ,求体积恰好是m时最大价值和体积最大是m时的最大价值有什么区别呢? 区别就在于dp[j]数组的初始化不同。
    1:当我们求的是体积恰好为m时的最大价值 ,我们需要把 dp[0] 初始化为 0 ,而dp[i] (1<=i<=m)初始化为负无穷 。为什么呢?因为此时dp[j]表示有0件物品且背包容量为j时的最大价值 。0件物品 ,只有当背包容量为0时 ,才能恰好装满背包,此时最大价值为0。负无穷表示其他条件下有 0件物品背包容量大于0时背包不可能装满 。
    2:当我们求的是体积不超过j时的最大价值 ,那么我们将dp[j] (j>=0)都初始化为0 。表示在有 0件物品且不要求装满的情况下 ,无论如何最大价值都是 0 。

    #include <stdio.h>
    int n,m,a,b,dp[1005];  
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            for(int j=m;j>=a;j--)
               {
                   dp[j]=max(dp[j],dp[j-a]+b);
               }
        }
        printf("%d",dp[m]);
        return 0;
    }
    

    2: 采药

    辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。

    为此,他想拜附近最有威望的医师为师。

    医师为了判断他的资质,给他出了一个难题。

    医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

    如果你是辰辰,你能完成这个任务吗?

    输入格式
    输入文件的第一行有两个整数T和M,用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。

    接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

    输出格式
    输出文件包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

    数据范围
    1≤T≤1000,
    1≤M≤100
    输入样例:
    70 3
    71 100
    69 1
    1 2
    输出样例:
    3

    #include <stdio.h>
    int n,m,a,b,dp[1005];
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d%d",&m,&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            for(int j=m;j>=a;j--)
            {
                dp[j]=max(dp[j],dp[j-a]+b);
            }
        }
        printf("%d",dp[m]);
        return 0;
    }
    

    完全背包

    1:完全背包问题
    有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。

    第 i 种物品的体积是 vi,价值是 wi。

    求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
    输出最大价值。

    输入格式
    第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

    接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

    输出格式
    输出一个整数,表示最大价值。

    数据范围
    0<N,V≤1000
    0<vi,wi≤1000
    输入样例
    4 5
    1 2
    2 4
    3 4
    4 5
    输出样例:
    10**

    **思路:**完全背包和01背包的区别就是每件物品可以取无数次 ,类比01背包 ,很容易的我们便得到了状态转移方程 dp[i][j]=max(dp[i-1][j],dp[i-1][j -k * v ]+k * w)。 和01背包一样 ,这里我们也可以进行空间 上的优化 。下面的代码已经十分简洁了,仔细想想为什么可以这样写。。。。。。因为每件物品可以取无数次,所以可以从小向大写。

    #include <stdio.h>
    int dp[1001],v,w,n,m;
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&v,&w);
            for(int j=v;j<=m;j++)
            {
                dp[j]=max(dp[j],dp[j-v]+w);
            }
        }
        printf("%d",dp[m]);
        return 0;
    }
    

    多重背包

    1 :多重背包问题

    有 N 种物品和一个容量是 V 的背包。

    第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

    求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
    输出最大价值。

    输入格式
    第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

    接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

    输出格式
    输出一个整数,表示最大价值。

    数据范围
    0<N,V≤100
    0<vi,wi,si≤100
    输入样例
    4 5
    1 2 3
    2 4 1
    3 4 3
    4 5 2
    输出样例:
    10

    **思路:**多重背包的特点就是每件物品有确定的数量 ,我们用dp[i][j]表示有i件物品体积最大是j时的最大价值 。状态转移方程为dp[i][j]=max(dp[i-1][j−c[i]∗k]+w[i]∗k,dp[i-1][j]); 同理 ,我们先可以对其进行空间上的优化·,以一维数组的形式储存数据。
    第一份代码如下:

    #include <stdio.h>
    int dp[1001],n,m,v,w,s;
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&v,&w,&s);
            for(int j=m;j>=v;j--)
            {
                for(int k=1;k*v<=j&&k<=s;k++)
                {
                    dp[j]=max(dp[j],dp[j-k*v]+k*w);
                }
            }
        }
        printf("%d",dp[m]);
        return 0;
    }
    

    上面的代码可以解决一定数据范围的多重背包问题 。但是时间复杂度过高 。在这里我们还可以采用二进制优化 。即一个正整数 n,可以f分解为(1 ,2 ,4, 8·····最大数)的形式 。什么意思呢?举个例子 :n的值为10 ,那么10可以分解为 1 ,2 ,4,3 。为什么这样分解呢?因为这样一来 ,10以内的所有正整数均能用我们分解出来的数表示出来 。这样 ,我们便可以将多重背包转变为01背包,对不对,是不是很巧妙。
    二进制优化的多重背包:

    #include <stdio.h>
    int dp[100000],n,m,v,w,s,t,k=0;
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    struct dalao
    {
        int V;
        int W;
    }p[100000];
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&v,&w,&s);
            t=1;
            while(t<=s)
            {
                s-=t;
                p[k].V=t*v;
                p[k].W=t*w;
                t*=2;
                k++;
            }
            if(s>0)
            {
                p[k].V=s*v;
                p[k].W=s*w;
                k++;
            }
        }
        for(int i=0;i<k;i++)
        {
            for(int j=m;j>=p[i].V;j--)
            {
                dp[j]=max(dp[j],dp[j-p[i].V]+p[i].W);
            }
        }
        printf("%d",dp[m]);
        return 0;
    }
    

    上面的代码很好 ,但是多重背包还有更好的优化方法 。即用单调队列优化 。但是由于本文篇幅有限 ,有兴趣的可以自行百度 。hahahah``。。。

    混合背包

    1:混合背包问题

    有 N 种物品和一个容量是 V 的背包。

    物品一共有三类:

    第一类物品只能用1次(01背包);
    第二类物品可以用无限次(完全背包);
    第三类物品最多只能用 si 次(多重背包);
    每种体积是 vi,价值是 wi。

    求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
    输出最大价值。

    输入格式
    第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

    接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

    si=−1 表示第 i 种物品只能用1次;
    si=0 表示第 i 种物品可以用无限次;
    si>0 表示第 i 种物品可以使用 si 次;
    输出格式
    输出一个整数,表示最大价值。

    数据范围
    0<N,V≤1000
    0<vi,wi≤1000
    −1≤si≤1000
    输入样例
    4 5
    1 2 -1
    2 4 1
    3 4 0
    4 5 2
    输出样例:
    8

    **思路:**混合背包其实就是上面三种背包的混合 。将多重背包转化为01背包 ,分为01背包和完全背包两种情况求解。

    #include <stdio.h>
    int dp[100000],v,w,n,m,s,t,k=0;
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    struct dalao 
    {
        int V;
        int W;
        int S;
    }p[100000];
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&v,&w,&s);
            if(s>0)
            {
                t=1;
                while(t<=s)
                {
                    s-=t;
                    p[k].V=v*t;
                    p[k].W=w*t;
                    p[k].S=-1;
                    k++;
                    t*=2;
                }
                if(s>0)
                {
                    p[k].V=v*s;
                    p[k].W=w*s;
                    p[k].S=-1;
                    k++;
                }
            }
            else
            {
                p[k].V=v;
                p[k].W=w;
                p[k].S=s;
                k++;
            }
        }
        for(int i=0;i<k;i++)
        {
            if(p[i].S==0)
            {
                for(int j=p[i].V;j<=m;j++)
                {
                    dp[j]=max(dp[j],dp[j-p[i].V]+p[i].W);
                }
            }
            else
            {
                for(int j=m;j>=p[i].V;j--)
                {
                    dp[j]=max(dp[j],dp[j-p[i].V]+p[i].W);
                }
            }
        }
        printf("%d",dp[m]);
        return 0;
    }
    
    

    分组背包

    1:分组背包问题
    有 N 组物品和一个容量是 V 的背包。

    每组物品有若干个,同一组内的物品最多只能选一个。
    每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

    求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

    输出最大价值。

    输入格式
    第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。

    接下来有 N 组数据:

    每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
    每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
    输出格式
    输出一个整数,表示最大价值。

    数据范围
    0<N,V≤100
    0<Si≤100
    0<vij,wij≤100
    输入样例
    3 5
    2
    1 2
    2 4
    1
    3 4
    1
    4 5
    输出样例:
    8

    思路: 类比于01背包,01背包有n件物品 ,每件物品有2个选择 ,即选或者不选 。我们可以把分组背包看成01背包 ,每组看成一个物品 ,对应的每组有s+1种选择 。第i组的每种选择都是建立在第i-1次的数据上。

    #include <stdio.h>
    int n,m,v[105],w[105],s,dp[105];
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&s);
            for(int i=0;i<s;i++)
            {
                scanf("%d%d",&v[i],&w[i]); //储存本组每种决策的代价和价值
            }
            for(int j=m;j>=0;j--)  //对于本组的每一个决策 都是基于上一组的数据 所以从大到小
            {
                for(int k=0;k<s;k++) //循环本组的物品 同组对于本组同一重量间竞争 每个重量 对应于本组的一个或0个物品
                {
                    if(v[k]<=j) //如果数据合法
                    {
                        dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
                    }
                }
            }
        }
        printf("%d",dp[m]);
        return 0;
    }
    

    **

    二维费用的背包问题

    **

    有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。

    每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。

    求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
    输出最大价值。

    输入格式
    第一行两个整数,N,V,M,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。

    接下来有 N 行,每行三个整数 vi,mi,wi,用空格隔开,分别表示第 i 件物品的体积、重量和价值。

    输出格式
    输出一个整数,表示最大价值。

    数据范围
    0<N≤1000
    0<V,M≤100
    0<vi,mi≤100
    0<wi≤1000
    输入样例
    4 5 6
    1 2 3
    2 4 4
    3 4 5
    4 5 6
    输出样例:
    8

    思路 : 其实就是在01背包的基础上 ,加了一层循环 ,写法和01背包一样。

    #include <stdio.h>
    int n,m,v,dp[1005][1005],a,b,c;
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d%d%d",&n,&v,&m);  //件数 体积 重量 
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);  //体积 重量 价值 
            for(int j=v;j>=a;j--)
            {
                for(int k=m;k>=b;k--)
                {
                    dp[j][k]=max(dp[j][k],dp[j-a][k-b]+c);
                }
            }
        }
        printf("%d",dp[v][m]);
        return 0;
    }
    

    背包问题求方案数

    有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

    第 i 件物品的体积是 vi,价值是 wi。

    求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

    输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。

    输入格式
    第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

    接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

    输出格式
    输出一个整数,表示 方案数 模 109+7 的结果。

    数据范围
    0<N,V≤1000
    0<vi,wi≤1000
    输入样例
    4 5
    1 2
    2 4
    3 4
    4 601
    输出样例:
    2

    思路: 因为我们要求方案数 ,为了方便记录 。我们用dp1[i]表示前i件物品体积恰好为j时的最大价值 。与01背包不同的是 ,我们需要另外开一个数组记录体积恰好为i时最大价值对应的方案数。对于每个物品 ,我们判断在体积恰好为j是否选择他,分为选他、不选他、选或者不选均是最大价值三种情况 。

    #include <stdio.h>
    int n,m,a,b,dp1[1005],dp2[1005],mod=1e9+7,s;  
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()   //dp1[i]表示体积恰好是i时的最大价值 dp2[i]表示体积恰好为i时最大价值对应的方案数
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            dp1[i]=-1e9;
        dp2[0]=1;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&a,&b);  //体积 价值
            for(int j=m;j>=a;j--)
            {
                s=0;
                int t=max(dp1[j],dp1[j-a]+b);
                if(t==dp1[j])   s+=dp2[j]; //判断那种方案价值最大 (有可能一样大)
                if(t==dp1[j-a]+b) s+=dp2[j-a];
                dp1[j]=t;
                dp2[j]=s%mod;
            }
        }
        int MAX=-1,ans=0;
        for(int i=0;i<=m;i++)
        {
            MAX=max(dp1[i],MAX);
        }
        for(int i=0;i<=m;i++)
        {
            if(MAX==dp1[i])
                ans+=dp2[i];
        }
        printf("%d",ans);
        return 0;
    }
    
  • 相关阅读:
    转来的:Diablo中的七大套装背景
    我的单元测试认识之路(下)
    《You can do it!》的chapter 2笔记
    一个枚举的简单应用
    仿百度的搜索下拉提示
    简单的 菜单当前选中 解决思路
    单元测试框架NUnit 之 Attributes特性(一)
    单元测试框架NUnit 之 constraints 约束
    分享个自己的ajax封装
    开源单元测试工具 Nunit
  • 原文地址:https://www.cnblogs.com/fxzemmm/p/14847976.html
Copyright © 2020-2023  润新知