HLOJ1366 Candy Box 动态规划(0-1背包改)

题目描述:

给出N个盒子(N<=100),每个盒子有一定数量的糖果(每个盒子的糖果数<=100),现在有q次查询,每次查询给出两个数k,m,问的是,如果从N个盒子中最多打开k个盒子(意思是打开1~k个盒子)能否使得糖果的总数恰好等于m,如果可以则输出Yes,否则输出No

题目分析:

对于普通的0-1背包我们经过优化之后用一个一维数组dp[j]表示的是前i个物品,容量为j时的可以获得的最大的价值,但是本题有些不同的是,我们求的dp[j]表示的是前i个盒子,容量恰好为j时,可以选择打开的盒子的最少个数(此处有一个不同之处在于,对于普通的0-1背包容量为j时的最大值可以是没有放满j的容量的,但是本题题意要求的是恰好放满m的容量,所以在推理动态转移方程的时候,如果dp[j-w[i]]的种数为0,则恰好为j就是不可能的

代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cmath>
 4 #include<string.h>
 5 using namespace std;
 6 
 7 int dp[10005];
 8 int w[105];
 9 
10 int main(){
11     int cnt = 1;
12     int n, q;
13     while(scanf("%d%d", &n, &q) != EOF){
14         printf("Case #%d:
", cnt++);
15         int sum = 0;
16         for(int i = 1; i <= n; i++){
17             scanf("%d", &w[i]);
18             sum += w[i];
19         } 
20         memset(dp, 0, sizeof(dp));
21         dp[w[1]] = 1;                     //对于第一个盒子而言,只有dp[w[1]] = 1这一种情况 
22         for(int i = 2; i <= n; i++){    //从第二个盒子开始遍历 
23             for(int j = sum; j >= w[i]; j--){
24                 if(dp[j] == 0){            //如果当前为0且前驱状态不为0,则构成当前容量可以打开的最少的盒子数自然是前一种的最少数+1 
25                     if(dp[j-w[i]] != 0){
26                         dp[j] = dp[j-w[i]] + 1;
27                     }
28                 }else{                    //如果当前状态不是0且前驱的状态也不为0 则取前驱+1,和当前状态的最小值 
29                     if(dp[j-w[i]] != 0){
30                         dp[j] = min(dp[j], dp[j-w[i]] + 1);
31                     }
32                 }
33             }
34             dp[w[i]] = 1;                //每次dp[w[i]]都是为1 这里需要补上,因为上述的动态转移方程当j==w[i]时是不处理的 
35         }
36         int k, m;
37         for(int i = 1; i <= q; i++){
38             scanf("%d%d", &k, &m);
39             if(dp[m] <= k && dp[m] != 0) printf("Yes
");
40             else printf("No
");
41         }
42     }    
43     return 0; 
44 } 
原文地址:https://www.cnblogs.com/findview/p/11837724.html