题目描述:
给出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 }