//动态规划:O(N*np),不适用于当np很大时。若np很大,则应该采用递归思想为 O(2^N)。
//因为DP的时间和空间都和np成正比,但是递归和np无关,所以算法选择因场景而变。
/* 题目 1:
给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。
输入为两行:
第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行为n个正整数A[i](32位整数),以空格隔开。
输出所求的方案数
假设现在有1元、2元、5元的纸币很多张,现在需要18块钱,你能给多少种找零方案
(可以认为是完全背包问题,即背包容量为18,物品体积分别为1、2、5)
还有公司出题目:给定一个数m,将m拆成不同的自然数的和的形式有多少种方案。
(这就是典型的01背包问题,背包容量为m,物品件数为k,这里面的k是隐含条件,
因为m最多由1+2+…+k得到,由此可以根据m求得物品件数的上限。)
//因为DP的时间和空间都和np成正比,但是递归和np无关,所以算法选择因场景而变。
/* 题目 1:
给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。
输入为两行:
第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行为n个正整数A[i](32位整数),以空格隔开。
输出所求的方案数
*/
#include<iostream>
using namespace std;
long long dp[1001];
int w[1001];
void findNumOfCase(int n, int sum, int *w){
if (n < 1 || sum < 0 || w[0] > sum)
return;
dp[0] = 1;
for (int i = 1; i <= sum; ++i)
dp[i] = 0;
for (int i = 0; i < n; ++i){
for (int j = sum; j >= 0; --j)
if (j >= w[i]) //----------- 注意:必须 >=
dp[j] = dp[j] + dp[j - w[i]];
}
cout << dp[sum] << endl;
}
/* 题目 2:假设现在有1元、2元、5元的纸币很多张,现在需要18块钱,你能给多少种找零方案
(可以认为是完全背包问题,即背包容量为18,物品体积分别为1、2、5)
还有公司出题目:给定一个数m,将m拆成不同的自然数的和的形式有多少种方案。
(这就是典型的01背包问题,背包容量为m,物品件数为k,这里面的k是隐含条件,
因为m最多由1+2+…+k得到,由此可以根据m求得物品件数的上限。)
*/
void FindMin1(int money, int *coin, int n)
{ //加上对money、coin、n的判断条件
int *res = new int[money + 1]();
for (int i = 1; i <= money; i++)
{
int min = i;
for (int j = 0; j < n; j++)
{
if (i >= coin[j]){
if (res[i - coin[j]] + 1 <= min)
min = res[i - coin[j]] + 1;
}
}
res[i] = min;
}
cout <<endl<< "----> "<<res[money] << endl;
delete[]res;
}
void main()
{
int sum = 20;
int w0[] = { 1, 2, 3, 5, 7, 10, 13 };
int n = sizeof(w0) / sizeof(w0[0]);
findNumOfCase(n, sum, w0);
int Money = 10;
int w1[] = { 2, 3, 5 };
int len = sizeof(w1)/sizeof(w1[0]);
FindMin1(Money, w1, len);
}