算法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 }

 

posted @ 2019-08-27 13:37  妮妮熊  阅读(650)  评论(0编辑  收藏  举报