0-1背包
• 给定n种物品和一背包,物品i的重量是wi,其价值是pi,背包的容量是M,问如何选择装入背包中的物品总价值最大?
背包的背负有上限,因此在这个上限内尽可能多的装东西,并且价值越多越好
• 0-1背包问题的解决办法
穷举算法
动态规划算法
贪心算法(未必获得最优解)
回溯算法
•动态规划算法
建立递归关系
c[i][m]=max{c[i-1][m], c[i-1][m-w[i]]+p[i]}
计算最优值
0-1背包的动态规划算法(周志光老师的课件代码):
1 # include<stdio.h> 2 int c[20][20]; //c[i][j],i件物品j的容量能放得最大价值 3 int w[20]; //体积(重量) 4 int p[20]; //价值 5 int x[20]; //第i见物品是否装入背包 6 //n件物品,背包容量m 7 int knapsack(int m,int n) //0-1背包动态规划求解 8 { 9 int i,j,k; 10 for(i=0; i<n+1; i++) 11 for(j=0; j<m+1; j++) 12 c[i][j]=0; 13 for(i=1; i<n+1; i++) 14 for(j=1; j<m+1; j++) 15 { 16 if(w[i]<=j) 17 { 18 if(p[i]+c[i-1][j-w[i]]>c[i-1][j]) 19 c[i][j]=p[i]+c[i-1][j-w[i]]; 20 else 21 c[i][j] = c[i-1][j]; 22 } 23 else 24 c[i][j] = c[i-1][j]; 25 } 26 return c[n][m]; 27 } 28 void traceBack(int n, int m) //构造最优解 29 { 30 int i; 31 for (i = n; i > 0; i--) 32 { 33 if (c[i][m] == c[i-1][m]) x[i] = 0; 34 else 35 { 36 x[i] = 1; 37 m -= w[i]; 38 } 39 } 40 printf("n个物品装入背包情况是:"); 41 for (i = 1; i < n+1; i++) printf(" %d",x[i]); 42 puts(""); 43 } 44 int main() 45 { 46 int i,n,m,j; 47 while(scanf("%d%d",&n,&m)!=EOF) 48 { 49 for(i=1; i<=n; i++) 50 scanf("%d%d",&w[i],&p[i]); 51 printf("背包能获得的最大价值 = %d ",knapsack(m,n)); 52 traceBack(n,m); 53 } 54 return 0; 55 }
•回溯算法
解空间定义
例如:0-1背包问题中,当n=3时,其解空间为:
(0,0,0),(0,0,1),(0,1,0),(0,1,1),
(1,0,0),(1,0,1),(1,1,0),(1,1,1)
解空间树
深度优先遍历
假设给定图G的初态是所有顶点均未曾访问过。在G中任选一顶点v为初始出发点(源点),则深度优先遍历可定义如下:首先访问出发点v,并将其标记为已访问过;然后依次从v出发搜索v的每个邻接点w。若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有路径相通的顶点(亦称为从源点可达的顶点)均已被访问为止。若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问为止。
剪枝函数
算法搜索至解空间树的任一结点时,总先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的系统搜索,逐层向其祖先结点回溯。
分类:条件约束、限界函数
0-1背包的回溯算法(周志光老师的课件代码):
1 # include<stdio.h> 2 int c; //背包能承受的最大重量 3 int n; //背包能装的最多的物品数 4 int cw; //背包现在的载重 5 int cp; //背包现在的价值 6 int bestp; //背包的最大价值 7 int w[50],p[50];//物品的重量和价值 8 int x[50],bestx[50]; 9 //最优状态下,如果第i个物品装入背包,那么bestx[i]=1;否则为0 10 int Bound(int i) //计算上界,限界函数 11 { 12 int cleft=c-cw;//剩余容量 13 int b=cp; 14 //以物品单位重量价值递减序装入物品 15 while(i<=n&&w[i]<=cleft) 16 { 17 cleft-=w[i]; 18 b+=p[i]; 19 i++; 20 } 21 if(i<=n) //装满背包 22 b+=p[i]/w[i]*cleft; 23 return b; 24 } 25 void backTrack(int i) 26 { 27 if(i>n) 28 { 29 if(bestp<cp) 30 { 31 for(int j=1; j<=n; j++) 32 bestx[j]=x[j]; 33 bestp=cp; 34 } 35 return; 36 } 37 if(cw+w[i]<=c) //搜索左子树 38 { 39 x[i]=1; 40 cw+=w[i]; 41 cp+=p[i]; 42 backTrack (i+1); 43 cw-=w[i]; 44 cp-=p[i]; 45 } 46 if(Bound(i+1)>bestp) //搜索右子树 47 { 48 x[i]=0; 49 backTrack (i+1); 50 } 51 } 52 int main() 53 { 54 int i; 55 while(scanf("%d%d",&n,&c)!=EOF) 56 { 57 for(i=1; i<=n; i++) 58 { 59 scanf("%d%d",&w[i],&p[i]); 60 } 61 cw=0; 62 cp=0; 63 bestp=0; 64 backTrack(1); 65 printf("背包能获得的最大价值 = %d ",bestp); 66 printf("n个物品装入背包情况是:"); 67 for(i=1;i<=n;i++) 68 printf(" %d",bestx[i]); 69 puts(""); 70 } 71 return 0; 72 }