求換錢的方法數

/*****************************************************************
 *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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章