换钱的方法数

题目描述

给定数组arr,设数组长度为n,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim,代表要找的钱数,求换钱的方法数有多少种。由于方法的种数比较大,所以要求输出对进行取模后的答案。

思路

动态规划:设置dp数组,dp[i][j]表示用arr[0…i]这些货币组成金额j的方法数。根据arr[i]是否使用,可以分为两种情况:

  1. arr[i]不使用。此时产生的方法数为dp[i-1][j],即使用arr[0…i-1]组成金额j的方法数。
  2. arr[i]使用,此时产生的方法数为dp[i][j-arr[i]],即使用arr[0…i]组成金额j-arr[i]的方法数。相当于已知使用arr[i]的情况,就可以在j中去掉一张arr[i],剩下的面值为j-arr[i],此时有dp[i][j] = dp[i][j-arr[i]]。这实际上是利用以求出的值堆算法时间复杂度的一种优化。
    因为题目要求的是方法总数,所以最终结果就是两种情况之和。

注意的问题

有一种情况是使用当前的货币无论如何也不能组成当前的金额,这种情况应该给dp值一个特殊的标记。当计算上述两种情况之和时,只有没有特殊标记的值才能参与运算。如果两个值都是特殊标记的,则结果也应该是特殊标记的。

代码实现

import java.util.Scanner;
public class Main{
    public static void main(String []args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int aim = scanner.nextInt();
        int []arr = new int [n];
        for(int i = 0 ; i<n ; i++){
            arr[i] = scanner.nextInt();
        }
        //dp[i][j]:使用arr[0...i]换金额为j的方法数
        long [][]dp = new long[n][aim+1];
        //答案为dp[n-1][aim]
        for(int j = 0 ; j<=aim ; j++){
            dp[0][j] = (j%arr[0] == 0) ?1:0;
        }
        for(int i = 1 ; i<n ; i++){
            dp[i][0] = 1;
        }
        //dp[i][j] = 
        //1.不用第i张 dp[i-1][j]
        //2.用第i张 dp[i][j-arr[i]]
        for(int i = 1 ; i<n ; i++){
            for(int j = 1 ; j<=aim; j++){
                dp[i][j] = dp[i-1][j];
                if(j-arr[i]>=0){
                    dp[i][j] += dp[i][j-arr[i]];   
                }
                dp[i][j] %= 1000000000 + 7;
            }
        }
        System.out.println(dp[n-1][aim]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值