0-1背包问题

/*
简单背包问题
问题描述
有一个背包,能盛放的物品总重量为s。
设有n件物品,其重量分别为w1,w2,...,wn.
希望从N件物品中选择若干件物品,所选物品的重量之和恰能放入该背包,
即所选物品的重量之和等于s。

若是可以选出满足条件的物品,则为有解,输出yes,然后输出该组物品的编号;
否则为无解,输出no。

注意:n件物品按照输入重量的顺序从1编号到n。


问题解析:
下面的代码用递归来解决问题。
算法根本思想是假设用布尔函数knap(s,n)表示n件物品放入可容质量为s的背包中是否有解
(当knap函数的值为真时,说明问题有解,其值为假时无解)。
我们可以通过输入s和n的值,根据它们的值可分为以下几种情况讨论:
(1)当s=0时可知问题有解(不选任何一件或者选了若干件之后剩余容量为0),即函数knap(s,n)的值为true;
(2)当s<0时,这时不可能放东西进入背包,所以函数值为false;
(3)当s>0且n<1时,即总物品的件数不足1,这时函数值为false。
(4)只有s>0且n≠1时才符合实际情况,这时又分为两种情况:
(a)选择的一组物体中不包括Wn,则knap(s,n)的解就是knap(s,n-1)的解.
(b)选择的一组物体中包括Wn,则knap(s,n)的解就是knap(s-Wn ,n-1)的解.
这样一组Wn 的值就是问题的最佳解.
这样就将规模为n的问题转化为规模为n-1的问题.

综上所述0- 1 背包问题的递归函数定义为:
knap( s, n) =true, s= 0  (不选任何一件或者选了若干件之后剩余容量为0)
knap( s, n) =false, s< 0
knap( s, n) =false, s> 0 且n< 1
knap( s, n) =knap( s, n- 1)或knap( s- Wn, n- 1) , s> 0 且n>= 1

采用此法求解0-1背包问题的时间复杂度为O(n)。
上述算法对于所有物品中的某几件恰能装满背包时能准确求出最佳解.

但一般情况是对于某一些物品无论怎么装都不能装满背包,
必须要按背包的最大容量来装.如物品件数为4,其质量分别为: 10, 2, 5, 4。
背包的容量为20, 则这四件物品无论怎么放都不能恰好装满背包,
但应能最大限度装, 即必须装下10, 5, 4 这三件物品, 这样就能得到最大质量19.
这一类问题以后再探讨。
*/
 1 #include <stdio.h>
 2 int w[1005];
 3 int c[1005];
 4 int maxN=1005;
 5 //下面这个函数是只返回0和1表示是否有解
 6 int f(int s,int n)//这里是要判断把n件物品放到容量s的背包是否有解。n同时是第n件物品的重量数组的下标。
 7 {
 8     if(s==0) return 1;//正好
 9     else if(s<0||(s>0&&n<0)) return 0; //n是物品件数,所以应判断n<0才算是没有物品
10     else if(f(s-w[n],n-1)||f(s,n-1)) return 1;//退出来再选下一个
11     else return 0;
12 }
13 //下面这个函数是在递归过程中不输出结果,递归结束,回到main再输出选中的物品的编号和重量
14 int f2(int s,int n)//这里是要判断把n件物品放到容量s的背包是否有解。n同时是第n件物品的重量数组的下标。
15 {
16     if(s==0) return 1;//正好
17     else if(s<0||(s>0&&n<0)) return 0; //n是物品件数,所以应判断n<0才算是没有物品
18     else if(f2(s-w[n],n-1)==1)
19     {
20         c[n]=1;
21         return 1;
22     }
23     else if(f2(s,n-1)==1) {c[n]=0;return 1;}//退出来再选下一个
24     else return 0;
25 }
26 //下面这个函数可以在递归过程中输出需要选择的物品的编号和重量
27 int f3(int s,int n)//这里是要判断把n件物品放到容量s的背包是否有解。n同时是第n件物品的重量数组的下标。
28 {
29     if(s==0) return 1;//正好
30     else if(s<0||(s>0&&n<0)) return 0; //n是物品件数,所以应判断n<0才算是没有物品
31     else if(f3(s-w[n],n-1)==1)
32     {
33         printf("number:%d  weight:%d\n",n+1,w[n]);
34         return 1;
35     }
36     else if(f3(s,n-1)==1) {c[n]=0;return 1;}//退出来再选下一个
37     else return 0;
38 }
39 int main()
40 {
41     freopen("snap_data/snap1.in","r",stdin);
42     freopen("snap_data/snap1.txt","w",stdout);
43     int i,s,n;
44     for(i=0;i<maxN;i++)
45     {
46         w[i]=0;
47         c[i]=0;
48     }
49     scanf("%d%d",&n,&s);
50     for(i=0;i<n;i++) scanf("%d",&w[i]);
51 
52     if(f2(s,n-1)==1)
53     {
54         //printf("yes\n");
55         for(i=0;i<n;i++)
56         {
57             if(c[i]==1)
58                 printf("number:%d  weight:%d\n",i+1,w[i]);
59         }
60     }
61     else printf("no\n");/**/
62 
63     /*if(f3(s,n-1)==1)
64     {
65         //printf("yes\n");
66     }
67     else printf("no\n");*/
68 
69     return 0;
70 }
View Code

若只是想简单地得到yes和no的结果,可以这样写:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int w[100]={0};
 4 int fun(int x,int T); //判断第x个物品是否可选,当前剩余容量T.  前x个物品(含第x个)刚好能凑够T返回1,否则返回0。
 5 int main()
 6 {
 7     int n,T,i;
 8     scanf("%d%d",&n,&T);
 9     for(i=1;i<=n;i++)
10         scanf("%d",&w[i]);
11     if(fun(n,T)==1) printf("yes\n");
12     else printf("no\n");
13     return 0;
14 }
15 int fun(int x,int T)//判断第x个物品是否可选,当前剩余容量T.  前x个物品(含第x个)刚好能凑够T返回1,否则返回0。
16 {
17     if(w[x]==T) return 1;   //假如第x个物品刚好把剩余容量填满,则有解
18     else if(x==1) return 0; //若已经从第n个判断到第1个,而第一个又不能刚好填满剩余容量,则当前的选择不合理
19     else return fun(x-1,T)||fun(x-1,T-w[x]);  // 依次尝试选择和不选择第x个物品
20 }
View Code

 

posted on 2015-08-20 16:21  华山青竹  阅读(665)  评论(0编辑  收藏  举报

导航