• 算法3:背包问题


    背包问题和01背包问题是很经典的关于动态规划和贪心算法的题目。

    这两个问题很相似,01背包是有一个容量为c的背包,装入一些质量为w[ ]的且价值为v[ ]的物品,每次只能选择放入或者不放,不能只放一部分某个物品。求出可以让背包装最大价值的一个x[ ],其中的每一项表示第 i 个物品是否要装入。

    背包问题跟01背包相似,但是可以装入部分商品。

    背包问题可以用贪心算法求解,01背包则要用动态规划。

    先来说01背包

    举个例子:

    c=5,即背包的容量是5.放入以下质量和价值的物品

    编号 质量w 价值v
    0 1 6
    1 3 12
    2 2 10

    根据前面的动态规划的解法,动态规划一般需要一个二维的数组存放每一步计算得到的动态结果,本例用矩阵 m 表示,行标表示商品编号 用 i 表示,列标表示变化的 j ,即容量

      0 1 2 3 4 5
    0 0 6 10 16 18 22
    1 0 0 10 12 12 22
    2 0 0 10 10 10 10

    本例的最佳装法解x应该是x={0,1,1},也就是装入第二个和第三个,总最大价值为12+10=22

    我写了一个简单的代码

     1 #include<iostream>
     2 using namespace std;
     3 int m[3][6];//动态规划中的矩阵
     4 int v[3]={12,10,6};//价值
     5 int w[3]={3,2,1};//质量
     6 int c=5;
     7 void knapsack()//v和w的长度都是3
     8 {
     9     int n=2;
    10     int jmax=min(w[n]-1,c);//防止某一个物件比总容量c更大
    11 
    12     //背包里只有第n个物件
    13     //后面的表达式里有计算i+1项的,所以要提前把最后一行计算出来
    14     for(int j=0;j<=jmax;j++)
    15         m[n][j]=0;
    16     for(int j=jmax+1;j<=c;j++)
    17         m[n][j]=v[n];
    18     //背包里的物件从n-1个开始增多
    19     for(int i=n-1;i>=0;i--)
    20     {
    21         int jmax=min(w[i]-1,c);
    22         for(int j=0;j<=jmax;j++)
    23             m[i][j]=m[i+1][j];//不装这个
    24         for(int j=jmax+1;j<=c;j++)
    25         {
    26             m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);//假如装入计算那个质量更大
    27         }
    28     }
    29     cout<<m[0][5]<<endl;
    30 }
    31 void traceback()
    32 {
    33     int x[3];
    34     for(int i=0;i<=1;i++)
    35     {
    36         if(m[i][5]==m[i+1][5])
    37             x[i]=0;
    38         else
    39         {
    40             x[i]=1;
    41         }
    42     }
    43     //计算最后一个物品是否放入 
    44     int sum=0;
    45     for(int i=0;i<=1;i++)
    46     {
    47         
    48         if(x[i]==1)
    49             sum+=v[i];
    50         
    51     }
    52     if(sum==m[0][5])
    53     {
    54         x[2]=0;
    55     }
    56     else
    57     {
    58         x[2]=1;
    59     }
    60     cout<<x[0]<<x[1]<<x[2];       
    61 }
    62 int main()
    63 {
    64     knapsack();
    65     traceback();
    66     return 0;
    67 }

    要理解01背包问题,一定要理解这个m矩阵,我换一个顺序再写一次,假如现在的物品顺序变成下面这样

    编号 质量w 价值v
    0 3 12
    1 2 10
    2 1 6

    那么矩阵m的变化如下:

      0 1 2 3 4 5
    0 0 6 10 16 18 22
    1 0 6 10 16 16 16
    2 0 6 6 6 6 6

    要把01背包问题弄清楚,一定要自己写一遍这个矩阵。这个矩阵的计算要靠下一行,所以最前面先计算好最后一行。前面的行的结果只能比后面的大,因为m[ i , j ] 表示背包容量为 j ,可以选择的物品从i,i+1……n。

    所有动态规划的问题都要很清楚的理解动态规划的动态矩阵的写法。

    牛客上也有这道题,我写了一个版本提交了,牛客网要求输出背包可以承受的最大价值。我把输入输出改成动态输入输出就可以了。

     1 #include<iostream>
     2 using namespace std;
     3 int knapsack(int c,int *w,int *v,int n)
     4 {
     5     n=n-1;//有n个物品,但是下标是从n-1开始计算的
     6     int m[n+1][c+1];
     7     int jmax=min(w[n]-1,c);//防止某一个物件比总容量c更大
     8 
     9     //背包里只有第n个物件
    10     //后面的表达式里有计算i+1项的,所以要提前把最后一行计算出来
    11     for(int j=0;j<=jmax;j++)
    12         m[n][j]=0;
    13     for(int j=jmax+1;j<=c;j++)
    14         m[n][j]=v[n];
    15     //背包里的物件从n-1个开始增多
    16     for(int i=n-1;i>=0;i--)
    17     {
    18         int jmax=min(w[i]-1,c);
    19         for(int j=0;j<=jmax;j++)
    20             m[i][j]=m[i+1][j];//不装这个
    21         for(int j=jmax+1;j<=c;j++)
    22         {
    23             m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);//假如装入计算那个质量更大
    24         }
    25     }
    26     return m[0][c];
    27 }
    28 void package()
    29 {
    30     int c,n,i=0;
    31     cin>>c>>n;//输入容量和物品的个数
    32     int w[n],v[n];
    33     for(int i=0;i<n;i++)
    34     {
    35         cin>>w[i]>>v[i];
    36     }
    37 
    38     cout<<knapsack(c,w,v,n);
    39 }
    40 int main()
    41 {
    42     package();
    43     return 0;
    44 }

    写了一个c++版本

     1 #include<iostream>
     2 #include<vector>
     3 using namespace std;
     4 int knapsack(int c,vector<int> w,vector<int> v,int n)
     5 {
     6     n=n-1;//有n个物品,但是下标是从n-1开始计算的
     7 
     8     vector<vector<int>> m(n+1);//m矩阵要从最后一行开始算,所以最好把m的大小先固定了
     9     int jmax=min(w[n]-1,c);//防止某一个物件比总容量c更大
    10 
    11     //背包里只有第n个物件
    12     //后面的表达式里有计算i+1项的,所以要提前把最后一行计算出来
    13     vector<int> temp;
    14     for(int j=0;j<=jmax;j++)
    15         temp.push_back(0);
    16     for(int j=jmax+1;j<=c;j++)
    17         temp.push_back(v[n]); 
    18     m[n]=temp;
    19     
    20     //背包里的物件从n-1个开始增多
    21     for(int i=n-1;i>=0;i--)
    22     {
    23         vector<int> tem;
    24         int jmax=min(w[i]-1,c);
    25         for(int j=0;j<=jmax;j++)
    26             tem.push_back(m[i+1][j]);//不装这个
    27         for(int j=jmax+1;j<=c;j++)
    28         {
    29             tem.push_back(max(m[i+1][j],m[i+1][j-w[i]]+v[i]));//假如装入计算哪个质量更大
    30         }
    31         m[i]=tem;
    32     }
    33     return m[0][c];
    34 }
    35 void package()
    36 {
    37     int c,n;
    38     cin>>c>>n;//输入容量和物品的个数
    39     vector<int> weight;
    40     vector<int> value;
    41      int temp1,temp2;
    42      for(int i=0;i<n;i++)
    43     {
    44         cin>>temp1>>temp2;
    45         weight.push_back(temp1);
    46         value.push_back(temp2);
    47     } 
    48 
    49     cout<<knapsack(c,weight,value,n);
    50 }
    51 int main()
    52 {
    53     package();
    54     return 0;
    55 }
  • 相关阅读:
    Advanced-REST-client安装
    啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
    mysql5.6 的st_distance 实现按照距离远近排序。
    Springboot读取配置文件及自定义配置文件
    mysql distinct
    SOAP XML报文解析
    提交post请求,参数为xml格式
    docker中tomcat日志输出自定义
    Vmware centos 虚拟机 磁盘扩容
    vim开发配置
  • 原文地址:https://www.cnblogs.com/neverland0718/p/11417836.html
Copyright © 2020-2023  润新知