2755:神奇的口袋,考点:动规、背包问题简单版
原题:http://bailian.openjudge.cn/practice/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[i][j]:考虑前i种,凑出的体积为j时选择物品的方式数目
边界条件:
dp[i][0]=1;dp[0][a[0]]=1(在第一个东西能装进去的前提下)
然后就可以从上往下进行计算了,
dp[i][j]先等于dp[i-1][j](不放进去),如果j可以让它放进去,就加上放进去的种数dp[i-1][j-a[i]]
自己写的代码如下:
#include <iostream> #include <cstring> #include <algorithm> using namespace std; int dp[25][41];//dp[i][j]表示考虑前i种,可用体积为j的时候不同的选择物品的方式数目 int a[25]; int main() { memset(dp, 0, sizeof(dp)); memset(a, 0, sizeof(a)); int n; cin >> n; for (int i = 0; i < n; i++) cin >> a[i]; for (int i = 0; i < n; i++) dp[i][0] = 1; if(a[0]<=40) dp[0][a[0]] = 1; for (int i = 1; i < n; i++) { for (int j = 1; j <= 40; j++) { dp[i][j] = dp[i - 1][j]; if (j >= a[i]) dp[i][j] += dp[i - 1][j - a[i]]; } } cout << dp[n - 1][40] << endl; return 0; }
种数、体积哪个做第一维变量都可以,如果是体积做第一维变量:
#include <iostream> using namespace std; int a[40]; int N; int Ways[50][40];//Ways[i][j]表示从前j种物品里凑出体积i的方法数 int main() { cin >> N; memset(Ways, 0, sizeof(Ways)); for (int i = 1; i <= N; ++i) { cin >> a[i]; Ways[0][i] = 1; } Ways[0][0] = 1; for (int w = 1; w <= 40; ++w) { for (int k = 1; k <= N; ++k) { Ways[w][k] = Ways[w][k - 1]; if (w - a[k] >= 0) Ways[w][k] += Ways[w - a[k]][k - 1]; } } cout << Ways[40][N]; return 0; }
还可以用我为人人型动规节省空间。int sum,判断sum[40]可达了几次。
#include <iostream> using namespace std; #define MAX 41 int main() { int n, i, j, input; int sum[MAX]; for (i = 0; i < MAX; i++) sum[i] = 0; cin >> n; for (i = 0; i < n; i++) { cin >> input; for (j = 40; j >= 1; j--) if (sum[j] > 0 && j + input <= 40) sum[j + input] += sum[j]; // 如果j有sum[j]种方式可达,则每种方式加上input就可达 j + input sum[input]++; } cout << sum[40] << endl; return 0; }