换钱的方法数

给定数组arr,arr中所有的值都为整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,在给定一个整数aim代表要找的钱数,求换钱的方法有多少种。


方法一:利用递归,时间复杂度O(aim^N)

arr=[10,b,c...], aim=100,分析过程如下:

1.用0张10元的货币,让[b,c......]组成剩下的100,最终方法数记为res1.

2.用1张10元的货币,让[b,c......]组成剩下的90,最终方法数记为res2.

3.用2张10元的货币,让[b,c......]组成剩下的80,最终方法数记为res3.

::::::

10.用10张10元的货币,让[b,c......]组成剩下的0,最终方法数记为res11.

那么res1+res2+res3+......+res11的值就是总的方法数。

int money1(int* arr, int len, int index,int aim)
{
    if(len==0 || aim<0)
        return 0;
    int res =0;
    if(index==len)
        res = (aim==0?1:0);
    else
        {
            for(int i=0; i*arr[index]<=aim; i++)
            {
                res += money1(arr,len,index+1,aim-arr[index]*i);
            }
        }

    return res;
}

方法二:动态规划方法,时间复杂度O(N*aim)

生成行数为N、列数为aim+1的矩阵dp,dp[i][j]的含义是在使用arr[0......i]货币的情况下,组成钱数j有多少种方法。dp[i][j]值得求法如下:

1.对于矩阵dp第一列的值dp[..][0],表示组成钱数为0的方法数,很明显是1种,也就是不使用任何货币。所以dp第一列的值统一设置为1.

2. 对于矩阵dp第一行的值dp[0][..],表示只能使用arr[0]这一种货币的情况下,组成钱的方法数,如,arr[0]=10时,能组成的钱数只有10,20,30.......所以,令dp[0][i*arr[0]]=1.

3. 出第一行和第一列外的其他位置,记为位置(i,j).。dp[i][j]的值是以下几个值的累加。

完全不用arr[i]货币,只使用arr[0...i-1]货币时,方法数为dp[i-1][j];

用1张arr[i]货币,剩下的钱用arr[0...i-1]货币组成,方法数为dp[i-1][j-arr[i]];

2张arr[i]货币,剩下的钱用arr[0...i-1]货币组成,方法数为dp[i-1][j-2*arr[i]];

::::::::

k张arr[i]货币,剩下的钱用arr[0...i-1]货币组成,方法数为dp[i-1][j-k*arr[i]]; (j-k*arr[i]>=0)

步骤3中,第一种情况方法数为dp[i-1][j],而第二种情况一直到第k种情况的方法数累加值其实就是dp[i][j-arr[i]]的值。所以步骤3可以简化为dp[i][j]=dp[i][j-arr[i]]+dp[i-1][j]

int money2(int* arr, int len, int aim)
{
    if(len==0 ||aim<0)
        return 0;
    int **dp;
    dp = new int*[len];
    for(int i=0; i<len; i++)
    {
        dp[i]=new int[aim+1];
    }
    for(int i=0; i<len; i++)
    {
        dp[i][0]=1;
    }
    for(int j=1; j<=aim; j++)
    {
        dp[0][j]=0;
    }
    for(int j=1; j*arr[0]<=aim; j++)
    {
        dp[0][j*arr[0]]=1;
    }


    for(int i=1; i<len; i++)
    {
        for(int j=1; j<=aim; j++)
        {
            if(j>=arr[i] )
            {
                dp[i][j]=dp[i][j-arr[i]]+dp[i-1][j];
            }
            else
                dp[i][j] = dp[i-1][j];
        }
    }
    int res = dp[len-1][aim];

    for(int i=1; i<len; i++)
    {
        delete []dp[aim+1];
    }
    delete []dp;

    return res;
}

方法三:时间复杂度为O(N*aim)的动态规划方法结合空间压缩技巧。额外的空间复杂度降至O(aim)

int money3(int* arr, int len, int aim)
{
    int *dp;
    dp = new int[aim+1];
    dp[0]=1;
    for(int i=1; i<=aim; i++)
    {
        dp[i]=0;
    }
    for(int j=1; j*arr[0]<=aim; j++)
    {
        dp[j*arr[0]]=1;
    }
    for(int i=1; i<len; i++)
    {
        for(int j=1; j<=aim; j++)
        {
            if(j-arr[i]>=0)
            {
                dp[j] = dp[j-arr[i]]+dp[j];
            }
            else
                dp[j]=dp[j];

        }
    }
    int res=dp[aim];
    return res;

}



三种方法的测试:


#include <iostream>
#include<stdlib.h>

int money1(int* arr, int len, int index,int aim);
int money2(int* arr, int len, int aim);
int money3(int* arr, int len, int aim);
using namespace std;
#define maxnum INT_MAX;
int main()
{
    int arr[] = {5,10,25,1};
    int aim = 15;
    size_t arr_len=sizeof(arr)/sizeof(int);
    int means1 = money1(arr,arr_len,0,aim);
    int means2 =money2(arr,arr_len,aim);
    int means3 =money3(arr,arr_len,aim);
    cout<<"means: "<<means1<<endl;
    cout<<"means: "<<means2<<endl;
    cout<<"means: "<<means3<<endl;
    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值