NYOJ289 苹果(经典的01背包问题)

115 阅读6分钟

题目:

苹果

时间限制: 3000 ms  |  内存限制: 65535 KB

难度: 3

    • 描述

    • ctest有n个苹果,要将它放入容量为v的背包。给出第i个苹果的大小和价钱,求出能放入背包的苹果的总价钱最大值。

      \

        • 输入
        • 有多组测试数据,每组测试数据第一行为2个正整数,分别代表苹果的个数n和背包的容量v,n、v同时为0时结束测试,此时不输出。接下来的n行,每行2个正整数,用空格隔开,分别代表苹果的大小c和价钱w。所有输入数字的范围大于等于0,小于等于1000。
        • 输出
        • 对每组测试数据输出一个整数,代表能放入背包的苹果的总价值。
        • 样例输入
        • 3 3
          1 1
          2 1
          3 1
          0 0
          
        • 样例输出
        • 2
          

\

方法一(二维):

代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
struct apple
{
    int w;//价钱
    int c;//重量
} a[1010];
int vv[1001][1001];//状态
int main()
{
    int n;//苹果的个数
    int v;//背包的容量
    while(~scanf("%d %d",&n,&v))
    {
        if(n==0&&v==0)return 0;
        for(int i=1; i<=n; i++)
            scanf("%d %d",&a[i].c,&a[i].w);//输入第i个苹果的价钱和重量
        //------------------------------------------------
        for(int i=0; i<=n; i++)
            vv[i][0]=0;
        for(int i=0; i<=v; i++)
            vv[0][i]=0;

        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=v; j++)
            {
                vv[i][j]=vv[i-1][j];
                if(j>=a[i].c)
                    vv[i][j]=max(vv[i-1][j],vv[i-1][j-a[i].c]+a[i].w);//前i件物品的最大承重量为j时所取得的最大价值=(前i-1件物品的承重量为j时所取得的最大价值)与(前i-1件物品的最大承重量为j减去第i件物品的重量的最大价值加上第i件物品的价值)作比较的值的大的一个
            }
        }
        //------------------------------------------------------
        printf("%d\n",vv[n][v]);
    }
    return 0;
}

对于测试样例,给出一组调试数据:

\

当面临放不放入背包的选择的时候,比较一下上一个的最大价值 和 当前放入背包的东西的重量减去要放的这个物品的重量时的最大价值 加上要放的这件东西的价值 两个选择中比较大的一个,所以当循环完所有的个数和容量后,所得到的就是最优解,最重要的是状态转移方程 

vv[i][j]=max(vv[i-1][j],vv[i-1][j-a[i].c]+a[i].w)

\

---------------------------------------------------------------------------------------------------------01背包-------------------------------------------------------------------------------------------------

01背包:

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

基本思路

这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。

用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物 品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

对于上面的代码,给出一组测试数据:

测试数据:
5 10
2 6
2 3
6 5
5 4
4 6

输出:

15

上面代码被线画出的部分,打成一张表可以表示为:



如图,上面的状态转移方程实质上就是弄出这样一份表:


状态转移方程:

f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ),  f[i-1,j] }
f[i,j]表示在前i件物品中选择若干件放在承重为 j 的背包中,可以取得的最大价值。
Pi表示第i件物品的价值

附一张图:



给出上面的调试内容的调试代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
struct apple
{
    int w;//价钱
    int c;//重量
} a[1010];
int vv[1001][1001];//状态
int main()
{
    int n;//苹果的个数
    int v;//背包的容量
    while(~scanf("%d %d",&n,&v))
    {
        if(n==0&&v==0)return 0;
        for(int i=1; i<=n; i++)
            scanf("%d %d",&a[i].c,&a[i].w);//输入第i个苹果的价钱和重量
        //------------------------------------------------
        for(int i=0; i<=n; i++)
            vv[i][0]=0;
        for(int i=0; i<=v; i++)
            vv[0][i]=0;

        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=v; j++)
            {
                printf("\ni=%d,j=%d\n",i,j);
                vv[i][j]=vv[i-1][j];
                printf("vv[%d][%d]=%d\n",i,j,vv[i][j]);
                if(j>=a[i].c)
                {
                    vv[i][j]=max(vv[i-1][j],vv[i-1][j-a[i].c]+a[i].w);//前i件物品的最大承重量为j时所取得的最大价值=(前i-1件物品的承重量为j时所取得的最大价值)与(前i-1件物品的最大承重量为j减去第i件物品的重量的最大价值加上第i件物品的价值)作比较的值的大的一个
                    printf("满足条件的vv[%d][%d]=%d\n",i,j,vv[i][j]);
                }

            }
        }
        //------------------------------------------------------
        printf("vv[%d][%d]=%d\n",n,v,vv[n][v]);
        printf("\n---------------------------------\n");
        for(int i=0; i<=n; i++)
        {
            for(int j=0; j<=v; j++)
                printf("[%d][%d]=%d ",i,j,vv[i][j]);
            printf("\n");
        }


    }
    return 0;
}

--------------------------------------------------------------------------------------------------------华丽的分割线---------------------------------------------------------------------------------------------------

方法二(一维):

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct apple
{
    int w;//价钱
    int c;//重量
} a[1010];
int main()
{
    int n;//苹果的个数
    int v;//背包的容量
    while(~scanf("%d %d",&n,&v))
    {
        if(n==0&&v==0)return 0;
        for(int i=0; i<n; i++)
            scanf("%d %d",&a[i].c,&a[i].w);//输入第i个苹果的价钱和重量
        int vv[1010];//第i位所能得到的最大总价值
        memset(vv,0,sizeof(vv));
        for(int i=0; i<n; i++)
        {
            vv[0]=0;
            for(int j=v; j>=a[i].c; j--)//j为当前背包的容量,当j大于当前的物品的重量的时候就循环
                vv[j]=max(vv[j],vv[j-a[i].c]+a[i].w);//第j个物品能达到的最大总价值为 第j-当前物品的重量+当前物品的价值 里面较大的一个
        }
        printf("%d\n",vv[v]);
    }
    return 0;
}

附上一个代码调试图,用的代码为上文01背包的测试代码:


附上调试代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct apple
{
    int w;//价钱
    int c;//重量
} a[1010];
int main()
{
    int n;//苹果的个数
    int v;//背包的容量
    while(~scanf("%d %d",&n,&v))
    {
        if(n==0&&v==0)return 0;
        for(int i=0; i<n; i++)
            scanf("%d %d",&a[i].c,&a[i].w);//输入第i个苹果的价钱和重量
        int vv[1010];//第i位所能得到的最大总价值
        memset(vv,0,sizeof(vv));
        for(int i=0; i<n; i++)
        {
            vv[0]=0;
            printf("\n");
            for(int j=v; j>=a[i].c; j--)//j为当前背包的容量,当j大于当前的物品的重量的时候就循环
            {
                printf("vv[%d]=%d,vv[%d-a[%d].c]+a[%d].w=%d,",j,vv[j],j,i,i,vv[j-a[i].c]+a[i].w);
                vv[j]=max(vv[j],vv[j-a[i].c]+a[i].w);//第j个物品能达到的最大总价值为 第j-当前物品的重量+当前物品的价值 里面较大的一个
                printf("i=%d,vv[%d]=%d\n",i,j,vv[j]);
            }

        }
        printf("%d\n",vv[v]);
        printf("\n--------------------------------\n");
        for(int i=0;i<v;i++)
            printf("vv[%d]=%d ",i,vv[v]);
        printf("\n");
    }
    return 0;
}


\



\