题目:
小明手里有n元钱全部用来买书,书的价格为10元,20元,50元,100元。
问小明有多少种买书方案?(每种书可购买多本)
输入格式
一个整数 n,代表总共钱数。
输出格式
一个整数,代表选择方案种数。
思路:
完全背包问题,且每种物品都有无穷多个,最主要的是注意集合的划分:
比如当前是第 i 种物品,则对于第 i 种物品的方案不只局限于选或者不选,还存在着选几个,选1个,2个,3个????等等;如下划分:
上图是从别处借的!!!!!!!!!!!!!!!!!!!!
此图的集合划分很好地描述了完全背包的集合情况:
下面举几个例子来说明:
- 不选i(第一个集合):f[i][j]+=f[i-1][j],表示的是第 i 种物品不选, 那么要想凑出价值 j 的话,就只能从前 i - 1种物品里面选了。莫法;所以说从前 i 个物品里选,凑出价值为 j 的方案 = 当前方案 + f[i-1][j];
- 第 i 种物品选 1个:f[i][j] += f[i-1][j-v[i]];表示第 i 种物品选择了一件,然后,还要球从前 i -1种物品里凑出价值为 j - v[i]的方案数!
代码:
深度优先搜索代码:
#include <iostream>
#include <cstring>
using namespace std;
const int prices[] = {10, 20, 50, 100}; // 不同面值书籍的价格
int dfs(int n, int money, int cur) {
if (money == n) return 1; // 刚好凑够n元,返回1种方案
if (money > n) return 0; // 钱数超过n元,无法凑够
int res = 0;
for (int i = cur; i < 4; i++) { // 枚举当前可选的书籍
res += dfs(n, money + prices[i], i); // 递归求解方案数
}
return res;
}
int main() {
int n;
cin >> n;
cout << dfs(n, 0, 0) << endl;
return 0;
}
朴素版本DP:
#include <iostream>
using namespace std;
const int N = 5, M = 1010;
int n = 4, m;
int v[N] = {0, 10, 20, 50, 100};
int f[N][M];
int main()
{
//input
cin >> m;
//dp
f[0][0] = 1;
for (int i = 1; i <= n; ++ i)
{
for (int j = 0; j <= m; ++ j)
{
for (int k = 0; v[i] * k <= j; ++ k)
{
f[i][j] += f[i - 1][j - v[i] * k];
}
}
}
//output
cout << f[n][m] << endl;
return 0;
}
滚动优化 DP代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e3 + 10;
int f[N];
int a[] = {10, 20, 50, 100};
int main()
{
int n;
cin >> n;
f[0] = 1; //表示从0件物品里面选,花费为0的方案数 = 1种,即什么都不选!
for (int i=0; i < 4; i++)
{
for (int j=a[i]; j <= n; j ++)
f[j] += f[j-a[i]];
}
cout << f[n] << endl;
return 0;
}