读《背包九讲》的几点感受,这里mark一下。
a.01背包问题题目描述:
有N件物品和一个容量为V 的背包。放入第i件物品耗费的空间是Ci,得到的价值是Wi。求解将哪些物品装入背包可使价值总和最大。
该问题的解决的一般思路是:写状态转移方程、动态规划。
具体实现的伪代码如下:
我们可以观察到上述伪码的时间复杂度和空间复杂度都是O(NV),对于时间复杂度暂时没有好的方法优化,但是空间复杂度却可以降低到O(V).
为什么把第二次循环掉个头,就可以用一维的F[]就能实现了?
其中“刚好装满”的例题:
POJ 3624 Charm Bracelet
1 #include <iostream> 2 #include <cmath> 3 #include <cstring> 4 using namespace std; 5 6 struct data{ 7 int weight ; //重量 8 int value ; //价值 9 }; 10 11 int main(){ 12 int num; //物品个数 13 int maxm; //最大能承受重量 14 cin>>num>>maxm; 15 data pack[num+1]; 16 for(int i=1;i<=num;i++){ //输入数据 17 cin>>pack[i].weight ; 18 cin>>pack[i].value ; 19 } 20 21 int result[maxm+1]; //初始化为0; 22 memset(result,0,sizeof(result)); 23 24 for(int i=1;i<=num;i++){ 25 for(int v=maxm;v>=pack[i].weight;v--) 26 { 27 result[v]=max( result[v], result[v-pack[i].weight]+pack[i].value ); 28 } 29 } 30 cout<<result[maxm]<<endl; 31 }
b.完全背包问题题目描述:
有N种物品和一个容量为V 的背包,每种物品都有无限件可用。放入第i种物品的耗费的空间是Ci,得到的价值是Wi。求解:将哪些物品装入背包,可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。
这里给出一个解决方案的伪码。仔细观察可以发现,这个和01背包问题的区别仅仅是第二个for循环遍历的方向不同而已,这就实现了可以取无限件。“看似困难的问题,却存在着巧妙而简单的的解决方法”,也许这正是算法的妙处。下面来解释这到底为什么:
这里曝上一道完全背包的问题,直接套用上面的伪码即可:
POJ 1384 Piggy-Bank
1 //我的代码 2 #include <stdio.h> 3 #include <string.h> 4 const int num=501,oo=1000000000; 5 int f[10001]={0},p[num],w[num]; 6 int main() 7 { 8 int t,n,weight_e,weight_f,i,j,weightpig; 9 scanf("%d",&t); 10 while (t--){ 11 12 scanf("%d%d",&weight_e,&weight_f); 13 for (i=1;i<=weight_f;i++) f[i]=oo; 14 weightpig=weight_f-weight_e; 15 scanf("%d",&n); 16 for (i=1;i<=n;i++) 17 scanf("%d%d",&p[i],&w[i]); 18 19 for (i=1;i<=n;i++){ 20 for (j=w[i];j<=weightpig;j++){ 21 if (f[j-w[i]]+p[i]<f[j]) f[j]=f[j-w[i]]+p[i]; 22 } 23 } 24 25 if (f[weightpig]==oo){ 26 printf("This is impossible. "); 27 } 28 else{ 29 printf("The minimum amount of money in the piggy-bank is %d. ",f[weightpig]); 30 } 31 } 32 }