【动态规划专题】5:换钱的方法数

《程序员代码面试指南--IT名企算法与数据结构题目最优解》 左程云 著

换钱的方法数

【题目】
给定数组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;
}
原文地址:https://www.cnblogs.com/music-liang/p/12116257.html