背包入门练习

POJ3624Charm Bracelet(01背包)

 01背包看看《背包九讲》第一讲就没问题。http://wenku.baidu.com/view/e5fd2015b7360b4c2e3f6416.html

View Code
 1 #include<stdio.h>
 2 int d[3405],w[3405];
 3  int n,m;
 4  int ZeroOnePack()  //01背包解法
 5  {
 6        int f[13000]={0},i,j;
 7        for( i=1; i<=n ; i++ )
 8        {
 9             for( j=m ; j>=w[i]; j-- )
10             {
11                   if(f[j]<f[j-w[i]]+d[i] ) //看是否更优
12                     f[j]=f[j-w[i]]+d[i];
13             }
14        }
15        return f[m];
16  }
17  int main()
18  {
19      while(~scanf("%d%d",&n,&m))
20      {
21          int i;
22          for(i=1;i<=n;i++)scanf("%d%d",&w[i],&d[i]);
23          printf("%d\n",ZeroOnePack());
24      }
25      return 0;
26  }

 

 

HDU1114Piggy-Bank(完全背包)

题目大意:有一个pig-bank(其实就是一个存钱罐),已知它的大小(由空罐子和装满后可算出)v,有n种钱币,每种价值是p,大小是w,问当刚好装满时最少有多少钱。

这个就是一个很明显的简单完全背包,只是和平时不同的是,他是求最小价值而不是最大。而且背包不是随意装而是要装满。

完全背包的概念可以借鉴上面的网址,这里面全部都有介绍。

     其中最重要的就是状态转移方程

                    f[j]=f[j-cost]+weight;

这里求最小价值,所以要更改的就是,初始两赋值为正无穷而不是负无穷(f[0]=0),比较时返回较小值。代码:

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<queue>
 5 #include<string>
 6 #include<stack>
 7 #include<cmath>
 8 #include<cctype>
 9 #include<iostream>
10 #include<set>
11 #include<algorithm>
12 #include<ctime>
13 #include<vector>
14 using namespace std;
15 #define mem(a) memset(a,0,sizeof(a))
16 const int MAXN=2000000007;
17 int dp[10000],c[505],w[505];
18 int v,n;
19 int min(int x,int y)//返回较小的值
20 {
21     return x<y?x:y;
22 }
23 int complete_pack()//完全背包
24 {
25     int i,j;
26     for(i=0;i<n;i++)
27         for(j=c[i];j<=v;j++)//c。。。v
28             dp[j]=min(dp[j],dp[j-c[i]]+w[i]);//状态转移方程
29     return dp[v]==MAXN?0:dp[v];
30 }
31 int main()
32 {
33     int cas;
34     scanf("%d",&cas);
35     while(cas--)
36     {
37         int x,y;mem(c);mem(w);
38         scanf("%d%d",&x,&y);
39         v=y-x;
40         scanf("%d",&n);
41         for(x=0;x<n;x++)scanf("%d%d",&w[x],&c[x]);
42         for(x=1;x<=v;x++)dp[x]=MAXN;//全部是不满足式,赋值为无穷大
43                                     //如果是秋最大价值,赋值为负无穷大
44         dp[0]=0;//  只有一个赋值为0                            
45         int result=complete_pack();
46         if(result)printf("The minimum amount of money in the piggy-bank is %d.\n",result);
47         else printf("This is impossible.\n");
48     }
49     return 0;
50 }

 

 

HDU4508湫湫系列故事——减肥记I(完全背包)

和上面一样的。给代码:

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define mem(a) memset(a,0,sizeof(a))
 5 using namespace std;
 6 int max(int a,int b)
 7 {
 8     return a>b?a:b;
 9 }
10 int main()
11 {
12     int f[100005],n,c[105],w[105];
13     while(~scanf("%d",&n))
14     {
15         int j,i,v;mem(f);
16         for(i=0;i<n;i++)scanf("%d%d",&w[i],&c[i]);
17         scanf("%d",&v);
18         for(i=0;i<n;i++)
19         {
20             for(j=c[i];j<=v;j++)f[j]=max(f[j-c[i]]+w[i],f[j]);
21         }
22         printf("%d\n",f[v]);
23     }
24     return 0;
25 }

 

 

HDU1171  Big Event in HDU(多重背包)

题目大意:有n种设施,每种的价值是v,数目是m,题目要求双方所分得的价值尽可能的接近。

最初一看上去还不觉得似乎是背包问题,可是一分析不难明白,设所有物品总价值是sum,那么每一方的价值就不会超过half=sum/2.这样就转化为了背包大小为half,每个物品大小和价值都是v的多重背包问题。即:

花费(cost=v;    价值(wealth)= v;  数目(num)=m

时间复杂度是O(half*(n个m[i]的和))

然后就是二进制优化,复杂度可减少到O(half*(log(n个m[i]的和))),大大降低。

实现方法:      for(对每一个物品)

                          if(cost*num〉=V)完全背包

                          else 

             k=1;     

             while  k<num

                01背包(k*cost,k*weight);

                num-=k;          

                k<<1;

            01背包(num*cost,num*weight)

给出代码:

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<queue>
 5 #include<string>
 6 #include<stack>
 7 #include<cmath>
 8 #include<cctype>
 9 #include<iostream>
10 #include<set>
11 #include<algorithm>
12 #include<ctime>
13 #include<vector>
14 using namespace std;
15 #define mem(a) memset(a,0,sizeof(a))
16 #define MAXN 1000000007
17 int v[1005],m[1005],n;
18 int f[1500000];
19 int sum,half;
20 int max(int a,int b)
21 {
22     return a>b?a:b;
23 }
24 void Zero_One_pack(int vv)//01背包求解
25 {
26     int i;
27     for(i=half;i>=vv;i--)
28         f[i]=max(f[i],f[i-vv]+vv);
29 }
30 void complete_pack(int vv)//完全背包求解
31 {
32     int i;
33     for(i=vv;i<=half;i++)
34         f[i]=max(f[i],f[i-vv]+vv);
35 }
36 int pack()//混合背包求解
37 {
38     int i;mem(f);
39     for(i=0;i<n;i++)
40     {
41         if(m[i]*v[i]>=half){//完全背包
42             complete_pack(v[i]);
43             continue;
44         }
45         int k=1;
46         while(k<m[i])//所谓的二进制模拟
47         {
48             Zero_One_pack(k*v[i]);
49             m[i]-=k;
50             k*=2;
51         }
52         Zero_One_pack(m[i]*v[i]);
53     }
54     return f[half];
55 }
56 int main()
57 {
58     int i;
59     while(~scanf("%d",&n)&&n>=0)
60     {
61         if(n==0){printf("0 0\n");continue;}
62         sum=0;
63         for(i=0;i<n;i++){
64             scanf("%d%d",&v[i],&m[i]);
65             sum+=v[i]*m[i];
66         }
67         half=sum/2;
68         int a=pack();
69         printf("%d %d\n",sum-a,a);
70     }
71     return 0;
72 }

 

 

 HDU2844Coins(多重)

这里有一段代码,思路是copy下来的,但是有一段不太懂。

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define mem(a) memset(a,0,sizeof(a))
 5 using namespace std;
 6 int f[100005],c[105],w[105],n,m;
 7 int max(int a,int b)
 8 {
 9     return a>b?a:b;
10 }
11 void z_o_pack(int v)
12 {
13     for(int i=m;i>=v;i--)
14         f[i]=max(f[i],f[i-v]+v);
15 }
16 void com_pack(int v)
17 {
18     for(int i=v;i<=m;i++)
19         f[i]=max(f[i],f[i-v]+v);
20 }
21 void pack()
22 {
23     for(int i=0;i<n;i++)
24     {
25         if(c[i]*w[i]>=m){com_pack(c[i]);continue;}
26         int k=1;
27         while(k<w[i])
28         {
29             z_o_pack(k*c[i]);
30             w[i]-=k;
31             k*=2;
32         }
33         z_o_pack(w[i]*c[i]);
34     }
35 }
36 int main()
37 {
38     while(~scanf("%d%d",&n,&m))
39     {
40         if(!n&&!m)break;
41         int i;
42         for(i=0;i<n;i++)scanf("%d",&c[i]);
43         for(i=0;i<n;i++)scanf("%d",&w[i]);
44         mem(f);
45         pack();
46         int count=0;
47         for(i=1;i<=m;i++){
48             if(f[i]==i)count++;//这里是为什么???
49         }
50         printf("%d\n",count);
51     }
52     return 0;
53 }

常规解法:在放进包里面时,每放一个,记录一个数据,这样就可以保证不遗漏任何一种可能。代码(转自http://blog.csdn.net/new_c_yuer/article/details/6670350):

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<utility>
 5 #include<string>
 6 #include<set>
 7 #include<vector>
 8 #include<stack>
 9 #include<algorithm>
10 #include<queue>
11 #include<cstdlib>
12 #include<map>
13 #include<cmath>
14 using namespace std;
15 const int M=100010;
16 const int inf=1<<29;
17 int cout;
18 bool bag[M];
19 int used[M],c[M],v[M];
20 int n,m;
21 
22 void work()
23 {
24     int i,j;
25     memset(bag,0,sizeof(bag));
26     bag[0]=1;
27     for(i=1;i<=n;i++)
28     {
29         memset(used,0,sizeof(used));
30         for(j=v[i];j<=m;j++)
31             if(!bag[j] && bag[j-v[i]] && used[j-v[i]]<c[i])
32             {
33                 bag[j]=true;
34                 used[j]=used[j-v[i]]+1;
35                 cout++;
36             }
37     }
38 }
39 
40 int main()
41 {
42     while(scanf("%d%d",&n,&m)==2)
43     {
44         if(n+m==0) break;
45         int i,j;
46         cout=0;
47         for(i=1;i<=n;i++)
48             scanf("%d",&v[i]);
49         for(i=1;i<=n;i++)
50             scanf("%d",&c[i]);
51         work();
52         printf("%d\n",cout);
53     }
54     return 0;
55 }

 

 

 HDU1712ACboy needs your help(分组背包)

首先很容易看出它是01背包的扩展,其次根据上面的思路,由于一组中只能选择一个,所以对每一个组找出最优解,状态转移方程:
                                                 f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]物品i属于第k组}

伪代码就是:            for 所有的组k

            for v=V...0

              for 所有的i属于k组

                 f[v]=max{f[v],f[v-c[i]]+w[i]}

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<queue>
 5 #include<string>
 6 #include<stack>
 7 #include<cmath>
 8 #include<cctype>
 9 #include<iostream>
10 #include<set>
11 #include<algorithm>
12 #include<ctime>
13 #include<vector>
14 #define mem(a) memset(a,0,sizeof(a))
15 #define MAXN 1000000007
16 int f[105],w[105][105];
17 int n,m;
18 int max(int a,int b)
19 {
20     return a>b?a:b;
21 }
22 void Zero_One_pack(int k)//第k组
23 {
24     for(int i=m;i>=1;i--)//01背包
25         for(int j=1;j<=m&&i>=j;j++)//它们都属于组k
26             f[i]=max(f[i],f[i-j]+w[k][j-1]);
27 }
28 int group_pack()//分组背包
29 {
30     mem(f);
31     for(int i=0;i<n;i++)
32         Zero_One_pack(i) ;
33     return f[m];
34 }
35 int main()
36 {
37     while(~scanf("%d%d",&n,&m))
38     {
39         if(!m&&!n)break;
40         for(int i=0;i<n;i++)
41             for(int j=0;j<m;j++)
42                 scanf("%d",&w[i][j]);
43         printf("%d\n",group_pack());
44     }
45     return 0;
46 }

 

 HDU3535AreYouBusy

最后加上一道综合性比较强的背包问题(对于入门选手来说,比如我==|,会很难做),他是一道分组了的混合三种背包的问题。就连我自己也很难做出来,我也是参考的其他大神的博客http://blog.csdn.net/x314542916/article/details/8723233,代码放在下面,希望读者好好体会每条语句的含义:(只能说自己太弱菜。。。)

View Code
 1 /*
 2     http://acm.hdu.edu.cn/showproblem.php?pid=3535 AreYouBusy
 3     混合背包,还分组了 ,三种类型的set  最多取一个,最少取一个和没有限制
 4 */
 5 
 6 #include <cstdio>
 7 #include <iostream>
 8 #include <string>
 9 #include <cstring>
10 #define CLR(c,v) (memset(c,v,sizeof(c)))
11 using namespace std;
12 
13 const int inf = -(1<<30);
14 const int INF =  (1<<30);
15 const int M   = 1e2 + 10;
16 
17 template <typename _T>
18 _T Max(_T a , _T b){
19     return (a>b)?(a):(b);
20 }
21 template <typename _T>
22 _T Max(_T a , _T b , _T c){
23     return (a>Max(b,c))?(a):(Max(b,c));
24 }
25 
26 int dp[M][M];
27 int v[M]; // :value
28 int c[M]; // :cost
29 
30 int main(){
31     //freopen("in.txt","r",stdin);
32     int n_set, max_cost;
33     while(cin >> n_set >> max_cost){
34         CLR(dp,0);
35         for (int i = 1 ; i <= n_set ; i++){
36             //0 stands for the sets that should choose at least 1 job to do, 
37             //1 for the sets that should choose at most 1 ,
38             //2 for the one you can choose freely
39             int n_case, case_type;
40             cin >> n_case >> case_type ;
41             for(int j = 1 ; j <= n_case ; j++){
42                 cin >> c[j] >> v[j];
43             }
44             if(case_type == 0){
45                 for (int k = 0 ; k <= max_cost ; k++) // 这样确保最少放一个物品
46                     dp[i][k] = inf;
47                 for (int j = 1 ; j <= n_case ; j++)
48                 for (int k = max_cost ; k >= c[j] ; k--){
49                     //** 如果是第一次选,则一定能加入数组中
50                     //** 如果不是第一次选,则当做普通01背包处理
51                     dp[i][k] = Max(dp[i][k] , dp[i-1][k-c[j]] + v[j] , dp[i][k-c[j]] + v[j]);  
52                 }
53             }else if(case_type == 1){
54                 for (int k = 0 ; k <= max_cost ; k++)
55                     dp[i][k] = dp[i-1][k]; // 为了保证全局最优解,初始化为上一次结果
56                 for (int j = 1 ; j <= n_case ; j++)
57                 for (int k = max_cost ; k >= c[j] ; k--){
58                     dp[i][k] = Max(dp[i][k], dp[i-1][k-c[j]] + v[j]);
59                 }
60             }else{
61                 for (int k = 0 ; k <= max_cost ; k++)
62                     dp[i][k] = dp[i-1][k];
63                 for (int j = 1 ; j <= n_case ; j++)
64                 for (int k = max_cost ; k >= c[j] ; k--){
65                     dp[i][k] = Max(dp[i][k], dp[i-1][k-c[j]] + v[j], dp[i][k-c[j]] + v[j]);
66                 }
67             }
68 
69         }
70         int res = Max(dp[n_set][max_cost] , -1);
71         cout << res << endl;
72     }
73     return 0;
74 }

 

背包问题实在是可以涉及太多,但实际比赛时能用的却很少,绝大多数都会与其他问题联合起来考察,或是题意隐含较深,所以还希望读者另外多加练习,才能随心所欲。当然,自己也还有很长的路要走。。。

posted @ 2013-04-13 19:33  再见~雨泉  阅读(495)  评论(0)    收藏  举报