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背包_子树

       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\n",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\n",bestp);
66         printf("n个物品装入背包情况是:");
67         for(i=1;i<=n;i++)
68         printf(" %d",bestx[i]);
69         puts("");
70     }
71     return 0;
72 }

 

把每一件简单的事情做好,就是不简单;把每一件平凡的事情做好,就是不平凡!相信自己,创造奇迹~~