背包问题之多重背包

http://soj.sysu.edu.cn/show_problem.php?pid=1001&cid=1769

其实感觉多重背包比01背包和完全背包都要难,每件物品的数量可能不止一件,还是求放入背包的物品的最大价值。

在背包九讲中,给出的动态转移方程为:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}

但是感觉还是考虑边界问题比较烦人,所以我想的是把多重背包和之前的01背包联系起来......不知道行不行

这里我写了一种一维dp数组的写法,在这题测试是tle的,因为这题的数据量太大,怎么解决后面再说......

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 
 7 int a[10];
 8 int w[10]={3, 5, 2, 6, 11, 8};
 9 double v[10]={0.01, 0.05, 0.10, 0.25, 0.50, 1};
10 double dp[10005];
11 //double dp[15][10005];
12 
13 int main()
14 {
15     int m;
16     while(scanf("%d", &m) != EOF)
17     {
18         memset(dp, 0, sizeof(dp));
19         int n=6;
20         for(int i=0; i<n; i++)
21             scanf("%d", &a[i]);
22         //一维 
23         for(int i=0; i<n; i++)
24             for(int j=0; j<a[i]; j++) //相当于把物品分成一件一件
25                 for(int k=m; k>=w[i]; k--)
26                        dp[k] = max(dp[k-w[i]]+v[i], dp[k]);
27         //二维  
28         /* 
29         for(int i=1; i<=n; i++)
30             for(int j=0; j<=m; j++)
31                 for(int k=0; k<=a[i]; k++)
32                 {
33                     if(j >= k*w[i])    
34                         dp[i][j] = max(dp[i-1][j-k*w[i]]+k*v[i], dp[i][j]); 
35                 }
36               */
37         printf("$%.2lf
", dp[m]);
38         //printf("$%.2lf
", dp[n][m]);
39     }
40     return 0;
41 }

看注释那一行就相当于把相同的这几件物品分成一件件,然后套用01背包的方法来解决!

那要怎么解决超时问题呢?

用到了二进制划分的办法,分成1,2,4,8,16...64.....(剩下的)

假如物品有12件,就分成1,2,4,5(这里的5就是剩下的),这样,如果我想拿7件,就用1+2+4来凑就好了,只用遍历3次,而不是7次!省了好多时间!

最后还是用01背包解决!

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 
 7 int a[15];
 8 double dp[15000];
 9 int weight[15]={3, 5, 2, 6, 11, 8};
10 double value[15]={0.01, 0.05, 0.10, 0.25, 0.50, 1};
11 int w[15000];
12 double v[15000];
13 
14 int main()
15 {
16     int N=6;
17     int m;
18     while(scanf("%d", &m) != EOF)
19     {
20         memset(dp, 0, sizeof(dp));
21         int count = 0;
22         for(int i=0; i<N; i++)
23             scanf("%d", &a[i]);
24             
25         for(int i = 0; i < N; ++i)
26         {
27             for(int j = 1; j <= a[i]; j <<= 1) // 二进制拆分
28             {
29                 w[count] = j * weight[i];
30                 v[count++] = j * value[i];
31                 a[i] -= j;
32             }
33             if(a[i] > 0)
34             {
35                 w[count] = a[i] * weight[i];
36                 v[count++] = a[i] * value[i];
37             }
38         }
39         for(int i = 0; i < count; ++i)  // 使用01背包
40             for(int j = m; j >= w[i]; j--)
41                 dp[j] = max(dp[j-w[i]]+v[i], dp[j]);
42         printf("$%.2lf
",dp[m]);
43     }  
44     return 0;
45 }                                 
原文地址:https://www.cnblogs.com/dominjune/p/4409220.html