神奇的口袋(百练2755)
Posted xwh-blogs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了神奇的口袋(百练2755)相关的知识,希望对你有一定的参考价值。
描述
有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a1,a2……an。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。
输入
输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。
输出
输出不同的选择物品的方式的数目。
样例输入
3
20
20
20
样例输出
3
思路:思路有两种,一种是递归的方法;另一种是动态规划dp。
①对于递归方法:对于挑选某个物品,都有2种选择:选择当前物品后再取看下一个物品或者是不选当前这个物品直接去看下一个物品,故方法数为这两种情况和。因此有挑选完第j个物品时达到体积i的取法总数为挑完第j-1(即前一个)物品时达到i值的方法总数(选完第j-1个物品后就达到了i值,所以对于第j个物品就不用选择了)和挑完第j-1(即前一个)物品时达到i-v[j]值的方法总数(选完第j-1个物品后就达到i-v[j]值,而v[j]值是第j个物品的体积;看完j-1个物品后但是离i值还差v[j],而第j个物品的值刚好是v[j],因此此时选择第j个商品进口袋)。
②对于动态规划:思路和法①基本接近。只不过一个是递归实现,一个是循环递推实现,可以根据①来设计主要的核心算法:
伪语言描述:
if 当前剩余容积i<第j个物品大小v[j] dp[i][j] = dp[i][j-1];//不放第j个物品的放法,容积不够只能不选 else if 当前剩余容积i>=第j个物品大小v[j] //容积足够 dp[i][j] =dp[i][j-1]+dp[i - v[j]][j - 1]; //对第j个物品选择和不选择两种做法 方法数加和
递归实现代码:
#include<stdio.h> #include<string.h> int a[30]; int Way(int x,int n) { if(x==0) return 1; if(n<=0) return 0; return Way(x,n-1)+Way(x-a[n],n-1);//可以选择选这个物品和不选这个物品 } int main() { int i,j,n,m; scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); printf("%d ",Way(40,n)); return 0; } //时间复杂度O(2^N)
dp实现代码:
#include<iostream> #include<cstdlib> #include<cstring> using namespace std; int main() { int n; int v[50]; int dp[50][50];//dp[i][j]表示从前j个物品中凑出体积为i的做法有多少种(即挑完j个抵达体积i的方法数) cin >> n; for (int i = 1; i <= n; ++i) { cin >> v[i]; } memset(dp, 0, sizeof(dp)); //边界条件:有物品,凑出体积为0的做法为1(一个物品都不放) for (int i = 0; i <= n; ++i) { dp[0][i] = 1; } for(int i = 1;i<=40;++i) for (int j = 0; j <= n; ++j) { dp[i][j] = dp[i][j-1];//不放第j个物品的放法 if (i >= v[j]) { dp[i][j] += dp[i - v[j]][j - 1]; } } cout << dp[40][n] << endl; return 0; }
以上是关于神奇的口袋(百练2755)的主要内容,如果未能解决你的问题,请参考以下文章