求换钱的方法数

/*****************************************************************
 *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;
}

发布了39 篇原创文章 · 获赞 3 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章