• 【动态规划专题】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;
    }
  • 相关阅读:
    Debug相关的一些小技巧
    <Information Storage and Management> 读书笔记 之二
    <<Information Storage and Management>>读书笔记 之三
    LINQ to SQL语句(2)之Select/Distinct【转】
    Asp.Net MVC实践 探索UrlRouting并分析UrlHelper (基于ASP.NET MVC Preview 3) 【转】
    MVC学习之分页 【转】
    在 ASP.NET MVC 项目中使用 WebForm 【转】
    Asp.net Mvc Codeplex Preview 5 第三篇 实现Action参数传递繁杂类型 【转】
    jQuery入门[1]-构造函数 【转】
    LINQ to SQL语句(1)之Where【转】
  • 原文地址:https://www.cnblogs.com/music-liang/p/12116257.html
Copyright © 2020-2023  润新知