The Coin Change Problem HckerRank(动态规划)

You are working at the cash counter at a fun-fair, and you have different types of coins available to you in infinite quantities. The value of each coin is already given. Can you determine the number of ways of making change for a particular number of units using the given types of coins?

For example, if you have  types of coins, and the value of each type is given as  respectively, you can make change for units in three ways: , and .

Complete the function getWays that takes the number of coin types and the value of each coin type as input, and return the number of ways to make change for  units using any number of coins.

Input Format

The first line contains two space-separated integers describing the respective values of  and , where: 
 is the number of units 
 is the number of coin types 
The second line contains  space-separated integers describing the respective values of each coin type :  (the list of distinct coins available in infinite amounts).

Constraints

  • Each  is guaranteed to be distinct.

Hints

Solve overlapping subproblems using Dynamic Programming (DP): 
You can solve this problem recursively but will not pass all the test cases without optimizing to eliminate the overlapping subproblems. Think of a way to store and reference previously computed solutions to avoid solving the same subproblem multiple times. * Consider the degenerate cases: 
- How many ways can you make change for  cents? - How many ways can you make change for  cents if you have no coins? * If you're having trouble defining your solutions store, then think about it in terms of the base case . - The answer may be larger than a -bit integer.

Output Format

Print a long integer denoting the number of ways we can get a sum of  from the given infinite supply of  types of coins.

Sample Input 0

4 3
1 2 3

Sample Output 0

4

Explanation 0

There are four ways to make change for  using coins with values given by :

Thus, we print  as our answer.

Sample Input 1

10 4
2 5 3 6

Sample Output 1

5

Explanation 1

There are five ways to make change for  units using coins with values given by :

Thus, we print  as our answer.

思路:

一眼望去就是DP,只是如何定义状态的问题。秒秒钟定义dp[i]表示总钱数为i的方法数,然后dp[i]  += dp[i-c[k]],k从0到sz。貌似很有道理的样子,不过这种方法没有考虑去重,即4 = 1+2+1,4=1+1+2这种思路算出来是两种方法。题目要求是无序的。所以我们必须区分出1,2,1和1,1,2序列的相同性。

定义状态dp[i][max]为总钱数为i,并且组成这些钱数的序列中的最大值是max,如dp[4][2],组成总钱数为4,并且要求序列中出现的最大值为2,则序列只能是2,1,1或2,2.dp[4][3]就只能是3,1我用一个序列的最大值,使得每一个不同的序列可以区分,相同的序列没有区别。

转移:dp[i][c[k]]:总钱数为i,序列中最大值为c[k],拿走一个最大值之后--》dp[i-c[k]][c[k]]+dp[i-c[k]][c[i]] (c[i]<c[k])

因为我们只要求在取i钱数的时候需要把c[k]作为最大值。然后到dp[i-c[k]][*]的时候相当于最大值已经被取了,剩下的i-c[k]的钱数只要每次取的钱数量小于等于c[k]即可。

可能你还不懂,那我举个例子,dp[7][3]表示取7的钱数,最大值为3的序列总数,其包括了dp[4][3],dp[4][2],dp[4][1].分别表示

在以后的4钱数里,最大每次的取值数为3,2,1.因为取钱数7的最大值3已经出现了,所以对后面的最大取值数不作要求。

所以在每次计算dp[7][3]的时候。需要计算dp[4][3],dp[4][2],dp[4][1].即需要计算所有的c[i],使得c[i]<c[k]。为了节省时间,我们可以令dp[4][3] = dp[4][3]+dp[4][2]+dp[4][1],这样只需要dp[7][3]+dp[4][3]了。为什么可以这样的原因很简单,每次你需要用到dp[4][3]的时候,你总要加上dp[4][2]和dp[4][1]。每次你要用到dp[4][2]的时候,你总要加上dp[4][1],因为最大值的需要前面已经满足了,后面的这些钱数就可以随意取小于等于那个最大值的值。

代码如下:

#include <bits/stdc++.h>

using namespace std;

long getWays(long n, vector < long > c){
    long  dp[500][500],x[500];
   memset(dp,0,sizeof(dp));
   sort(c.begin(),c.end());//记得要排序
    int sz = c.size();
    for(int i=0;i<sz;i++){
        int a = c[i];
        dp[a][a] = 1;
    }
    memset(x,0,sizeof(x));
    for(int i=1;i<=n;i++){
        for(int k=0;k<sz;k++){
            if(i>=c[k]){
                long a = i-c[k];
                long aa = 0;
                if(a<c[k])aa = dp[a][c[sz-1]];
                else  aa = dp[a][c[k]];
                dp[i][c[k]] += aa;
            }
            if(k!=0)dp[i][c[k]]+=dp[i][c[k-1]];
        }
    }
    return dp[n][c[sz-1]];
}


int main() {
    int n;
    int m;
    cin >> n >> m;
    vector<long> c(m);
    for(int c_i = 0; c_i < m; c_i++){
       cin >> c[c_i];
    }
    // Print the number of ways of making change for 'n' units using coins having the values given by 'c'
    long ways = getWays(n, c);
    cout<<ways<<endl;
    return 0;
}

2.找到一种思路更为清晰的办法,仔细观察,可以把总钱数看为一个背包,各个取钱数分别为物品的体积,且可以无限取,这就是一个完全背包的模型,可以用dp[i]表示钱数为i时的取法,dp[i] = dp[i] + dp[i-c[j]];其中,c[k]是第k个的钱数。

代码如下:

/*by kzl*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>

using namespace std;
const int maxx = 1e5+500;
const int INF = 0x3f3f3f3f;
typedef long long LL;


long getWays(long n, vector < long > c){
    long dp[500];
    memset(dp,0,sizeof(dp));
    dp[0] = 1;
    int sz = c.size();
    for(int i=0;i<sz;i++){
        for(int j=1;j<=n;j++){
            if(j>=c[i])dp[j] += dp[j-c[i]];
        }
    }
    return dp[n];
}

int main(){
    int n;
    int m;
    cin >> n >> m;
    vector<long> c(m);
    for(int c_i = 0; c_i < m; c_i++){
       cin >> c[c_i];
    }
    // Print the number of ways of making change for 'n' units using coins having the values given by 'c'
    long ways = getWays(n, c);
    cout<<ways<<endl;
    return 0;

return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值