/*****************************************************************
*description:求换钱的方法数
* 数组arr所有元素都为正数且不重复,每个元素代表一种面值的货币。
* 每种面值货币可以使用任意多张,求组成tar的方法数。
*****************************************************************/
#include <iostream>
#include <vector>
using namespace std;
//方法一:暴力递归。时间复杂度O(tar^N)(最坏情况)
//arr[0]使用0次,arr[1,2,3..]组成tar-0*arr[0]的方法数res1
//arr[0]使用1次,arr[1,2,3..]组成tar-1*arr[0]的方法数res2
//...
//arr[0]使用tar/arr[0]次,arr[1,2,3..]组成tar-tar/arr[0]*arr[0]的方法数resk
//则总次数=res1+res2+...+resk.
int process_1(const vector<int> &arr, int index, int tar)
{
if (index == arr.size())//递归终止条件
{
if (tar == 0)
return 1;
else
return 0;
}
int res = 0;
for (int i = 0; i <= tar / arr[index]; i++)
res += process_1(arr, index + 1, tar - arr[index] * i);
return res;
}
int Coins_1(const vector<int> &arr, int tar)
{
return process_1(arr, 0, tar);
}
//方法二:记忆化搜索。时间复杂度O(N*tar^2)
//由于暴力递归存在重复计算,所以将每次递归结果记录下来,下次计算之前先查询是否计算过。
//创建大小为
int process_2(const vector<int> &arr, vector<vector<int>> &tmp, int index, int tar)
{
if (tmp[index][tar] == -1)
{
int res = 0;
if (index == arr.size())
{
if (tar == 0)
res = 1;
else
res = 0;
}
else
{
for (int i = 0; i <= tar / arr[index]; i++)
res += process_2(arr, tmp, index + 1, tar - arr[index] * i);
}
tmp[index][tar] = res;
}
return tmp[index][tar];
}
int Coins_2(const vector<int> &arr, int tar)
{
vector<vector<int>> tmp(arr.size() + 1, vector<int>(tar + 1, -1));
return process_2(arr, tmp, 0, tar);
}
//方法三:动态规划。时间复杂度O(N*tar^2)
//创建大小为N*(tar+1)的二维数组tmp。tmp[i][j]表示arr[0...i]组成j的方法数。
//第一列tmp[i][0]:arr[i]组成0的方法,均为1.
//第一行tmp[0][j]:arr[0]组成j的方法,arr[0]整数倍的j对应tmp[0][j]为1,其他为0。
//其他位置tmp[i][j]=tmp[i-1][j]+sum(tmp[i-1][j-k*arr[i]]).k取所有不越界的值j-k*arr[i]。
int Coins_3(const vector<int> &arr, int tar)
{
const int N = arr.size();
vector<vector<int>> tmp(N, vector<int>(tar + 1, 0));
for (int i = 0; i < N; i++)
tmp[i][0] = 1;
for (int j = 1; j < tar + 1; j++)
{
if (j % arr[0] == 0)
tmp[0][j] = 1;
else
tmp[0][j] = 0;
}
for (int i = 1; i < N; i++)
{
for (int j = 1; j < tar + 1; j++)
{
for (int k = j; k >= 0; k -= arr[i])
{
tmp[i][j] += tmp[i-1][k];
}
}
}
return tmp[N - 1][tar];
}
//方法四:优化动态规划。时间复杂度O(N*tar)
//创建大小为N*(tar+1)的二维数组tmp。tmp[i][j]表示arr[0...i]组成j的方法数。
//第一列tmp[i][0]:arr[i]组成0的方法,均为0.
//第一行tmp[0][j]:arr[0]组成j的方法,arr[0]整数倍的j对应tmp[0][j]为1,其他为0。
//其他位置tmp[i][j]=tmp[i-1][j]+sum(tmp[i-1][j-k*arr[i]]).k取所有不越界的值j-k*arr[i]。
//而sum(tmp[i-1][j-k*arr[i]])=tmp[i][j-arr[i]]。所以tmp[i][j]=tmp[i-1][j]+tmp[i][j-arr[i]]
int Coins_4(const vector<int> &arr, int tar)
{
const int N = arr.size();
vector<vector<int>> tmp(N, vector<int>(tar + 1, 0));
for (int i = 0; i < N; i++)
tmp[i][0] = 1;
for (int j = 1; j < tar + 1; j++)
{
if (j % arr[0] == 0)
tmp[0][j] = 1;
else
tmp[0][j] = 0;
}
for (int i = 1; i < N; i++)
{
for (int j = 1; j < tar + 1; j++)
{
tmp[i][j] = tmp[i-1][j];
tmp[i][j] += j - arr[i] >= 0 ? tmp[i][j-arr[i]] : 0;
}
}
return tmp[N - 1][tar];
}
int main()
{
vector<int> arr;
arr.push_back(5);
arr.push_back(10);
arr.push_back(1);
arr.push_back(25);
cout << Coins_4(arr, 15) << endl;
return 0;
}
求换钱的方法数
最新推荐文章于 2020-12-04 15:11:35 发布