【動態規劃專題】5:換錢的方法數

《程序員代碼面試指南--IT名企算法與數據結構題目最優解》 左程雲 著ios

換錢的方法數程序員

【題目】
給定數組arr, arr中全部的值都爲正數且不重複。
每一個值表明一種面值的貨幣,每種面值的貨幣能夠使用任意張,
再給定一個整數aim,表明要找的錢數,求換錢有多少種方法。面試

【舉例】
arr=[5,10,25,1],aim=0
組成0元的方法只有1種,就是全部面值的貨幣都不用。因此返回1.算法

arr=[j5,10,25,1],aim=15
組成15元的方法有6種,
5Y*3
10Y*1+5Y*1
10Y*1+1Y*5
1Y*10+5Y*1
5Y*2+1Y*5
1Y*15數組

任何方法都沒法組成2元,因此aim=2會返回0.數據結構

 

#include <algorithm>
#include <iostream>
#include <stack>
#include <vector>
#include <exception>
using namespace std;

void PrintMap(int ** map, int rows, int cols)
{
    cout << "PrintMap--------------start" << endl;
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            cout << map[i][j] << ",";
        }
        cout << endl;
    }
    cout << "PrintMap--------------End" << endl;
    cout << endl;
}

 

///////////////////////////////////////////////////////解法1:暴力遞歸
int process1(int *arr, int length, int index, int aim)
{
    int res = 0;
    if (index == length)
    {
        res = aim == 0 ? 1 : 0;
    }
    else
    {
        for (int i = 0; arr[index] * i <= aim; i++)
        {
            res += process1(arr, length, index + 1, aim - arr[index] * i);
        }
    }
    return res;
}

int coins1(int *arr, int length, int aim)
{
    if (arr == nullptr || length <= 0 || aim <= 0)
    {
        return 0;
    }
    return process1(arr, length, 0, aim);
}

 

////////////////////////////////////////////////解法2:暴力遞歸優化版。記憶搜索
////p(index, aim)這樣的一個遞歸過程.用map[index][aim]保存已經計算過的遞歸過程的結果
int process2(int *arr, int length, int index, int aim, int **map, int rows, int cols)
{
    int res = 0;
    if (index == length)
    {
        res = aim == 0 ? 1 : 0;
    }
    else
    {
        int mapValue = 0;
        for (int i = 0; arr[index] * i <= aim; i++)
        {
            mapValue = map[index + 1][aim - arr[index] * i];
            
            if (mapValue != 0)
            {
                res += mapValue == -1 ? 0 : mapValue;
            }
            else
            {
                res += process2(arr, length, index + 1, aim - arr[index] * i, map, rows, cols);
            }
        }
    }

    map[index][aim] = res == 0 ? -1 : res;

    return res;
}


int coins2(int *arr, int length, int aim)
{
    if (arr == nullptr || length <= 0 || aim <= 0)
    {
        return 0;
    }
    int**map = new int*[length+1];
    for (int i = 0; i < length + 1; i++)
    {
        map[i] = new int[aim + 1];
    }
    for (int i = 0; i < length + 1; i++)
    {
        for (int j = 0; j < aim + 1; j++)
        {
            map[i][j] = 0;
        }
    }

    int iResult = process2(arr, length, 0, aim, map, length+1, aim+1);

    PrintMap(map, length + 1, aim + 1);

    return iResult;
}

 

////////////////////////////////////////////////////////////////解法3:動態規劃分析的方法、抽象出dp[i][j]是如何由上一行獲得的
//dp[i][j]的含義是,在使用arr[0...i]貨幣的狀況下,組成錢數j有多少種方法。
//....
//dp[N-1][aim]的值就是最終結果
int coins3(int *arr, int length, int aim)
{
    if (arr == nullptr || length <= 0 || aim <= 0)
    {
        return 0;
    }
    int**dp = new int*[length];
    for (int i = 0; i < length; i++)
    {
        dp[i] = new int[aim + 1];
    }
    for (int i = 0; i < length; i++)
    {
        for (int j = 0; j < aim + 1; j++)
        {
            dp[i][j] = 0;
        }
    }

    ////第1列都爲0.dp[i][0] 都爲1,表示 組成錢數0的方法只有1種
    for (int i = 0; i < length; i++)
    {
        dp[i][0] = 1;
    }

    //第1行中的某些項.dp[0][j],只使用arr[0]貨幣,能夠組成哪些數
    for (int j = 1; j * arr[0] < aim+1; j++)
    {
        dp[0][j*arr[0]] = 1;
    }

    int num = 0;
    for (int i = 1; i < length; i++)
    {
        for (int j = 1; j < aim + 1; j++)
        {
            num = 0;
            for (int k = 0; j - arr[i] * k >= 0; k++)
            {
                num += dp[i - 1][j - arr[i] * k];
            }
            dp[i][j] = num;
        }
    }

    int iResult = dp[length - 1][aim];

    PrintMap(dp, length, aim + 1);

    return iResult;
}

 

////////////////////////////////////////////////解法4:動態規劃,再次抽象
//dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]]
//
int coins4(int *arr, int length, int aim)
{
    if (arr == nullptr || length <= 0 || aim <= 0)
    {
        return 0;
    }
    int**dp = new int*[length];
    for (int i = 0; i < length; i++)
    {
        dp[i] = new int[aim + 1];
    }
    for (int i = 0; i < length; i++)
    {
        for (int j = 0; j < aim + 1; j++)
        {
            dp[i][j] = 0;
        }
    }

    ////第1列都爲0.dp[i][0] 都爲1,表示 組成錢數0的方法只有1種
    for (int i = 0; i < length; i++)
    {
        dp[i][0] = 1;
    }

    //第1行中的某些項.dp[0][j],只使用arr[0]貨幣,能夠組成哪些數
    for (int j = 1; j * arr[0] < aim + 1; j++)
    {
        dp[0][j*arr[0]] = 1;
    }

    int num = 0;
    for (int i = 1; i < length; i++)
    {
        for (int j = 1; j < aim + 1; j++)
        {
            dp[i][j] = dp[i - 1][j];
            dp[i][j] += j - arr[i] >= 0 ? dp[i][j - arr[i]] : 0;
         }
    }

    int iResult = dp[length - 1][aim];

    PrintMap(dp, length, aim + 1);

    return iResult;
}

 

////////////////////////////////////////////////解法5:動態規劃,空間壓縮
//dp[i][j] = dp[i-1][j] + dp[i][j-arr[i]]
int coins5(int *arr, int length, int aim)
{
    if (arr == nullptr || length <= 0 || aim <= 0)
    {
        return 0;
    }
    int*dp = new int[aim+1];
    for (int j = 0; j < aim + 1; j++)
    {
        dp[j] = 0;
    }

    //第1行中的某些項.dp[0][j],只使用arr[0]貨幣,能夠組成哪些數
    for (int j = 1; j * arr[0] < aim + 1; j++)
    {
        dp[j*arr[0]] = 1;
    }

    for (int i = 1; i < length; i++)
    {
        for (int j = 1; j < aim + 1; j++)
        {
            dp[j] += j - arr[i] >= 0 ? dp[j - arr[i]] : 0;
        }
    }

    int iResult = dp[aim];

    return iResult;
}

 

////===============測試用例====================
void test1()
{
    int arr[4] = { 5, 10, 25, 1 };
//    int iResult1 = coins1(arr, sizeof(arr) / sizeof(int), 15);
//    cout << "iResult1:" << iResult1 << endl;


    //int iResult2 = coins2(arr, sizeof(arr) / sizeof(int), 15);
    //cout << "iResult2:" << iResult2 << endl;


//    int iResult3 = coins3(arr, sizeof(arr) / sizeof(int), 15);
//    cout << "iResult3:" << iResult3 << endl;

//    int iResult4 = coins4(arr, sizeof(arr) / sizeof(int), 15);
//    cout << "iResult4:" << iResult4 << endl;

    int iResult5 = coins5(arr, sizeof(arr) / sizeof(int), 15);
    cout << "iResult5:" << iResult5 << endl;

}


int main()
{
    test1();

    cout << endl;
    system("pause");
    return 0;
}
相關文章
相關標籤/搜索