硬币拼凑(01背包和完全背包)

有n1+n2种面值的硬币,其中前n1种为普通币,可以取任意枚,后n2种为纪念币, 每种最多只能取一枚,每种硬币有一个面值,问能用多少种方法拼出m的面值?

 输入描述:
第一行输入三个整数n1,n2,m        n1,n2<=1000 m<=100000
第二行输入n1个整数表示普通币的面值
第三行输入n2个整数表示纪念币的面值
不同的硬币面值可能相同


输出描述:

使用编号不同但面值相同的硬币算不同的拼法
输出用多少种方法拼出m的面值,由于答案过大,对1e9 + 7取模
 
 
第一个问题:只考虑币数组,拼成指定钱数,有多少种方法
dp[i][j]:0...i 自由选,搞定j元有多少种方法

 

普遍位子如何填:

1:i号币我不用,->dp[i-1][j]

2:i号币我用,->dp[i-1][j-str[i]]

dp[i][j]=1+2

 

 

 

 /**
     dp[i][j]任意使用0---i号币,能组成j的钱来
     【1,3,2,4】 8
     dp[N-1][j]最事一行的意义是任意使用n个币,能组成的j钱
     */
    public static long[][] getDpOne(int[] arr,int money) {
        if (arr == null || arr.length == 0) {
            return null;
        }

        long[][] dp = new long[arr.length][money + 1];
        for (int i = 0; i < arr.length; i++) {
            dp[i][0] = 1;
        }

        if (arr[0] <= money) {
            dp[0][arr[0]] = 1;
        }

        for (int i = 1; i < arr.length; i++) {
            for (int j = 1; j <= money; j++) {
                dp[i][j] = dp[i - 1][j];
                dp[i][j] += j - arr[i] > 0 ? dp[i - 1][j - arr[i]] : 0;
                //输出用多少种方法拼出m的面值,由于答案过大,对1e9 + 7取模
                dp[i][j] = dp[i][j] % 1000000007;
            }
        }

        return dp;
    }

  

 

第二个问题:只考虑券数组,拼成指定钱数,有多少种方法,每张券可以无限取

   

 

券在0....3上任意选,可以搞定100的方法数

 

 

 dp[i][j]:券在0....i上任意选,可以搞定j的方法数

 

 

 

 /**
     dp[i][j]任意使用0---i号币(可以使用任意张),能组成j的钱来
     【1,3,2,4】 8
     dp[N-1][j]最事一行的意义是任意使用n个币,能组成的j钱
     */
    public static long[][] getDpArb(int[] arr,int money){
        if(arr==null||arr.length==0){
            return null;
        }
        long[][] dp=new long[arr.length][money+1];

        for(int i=0;i<arr.length;i++){
            dp[i][0]=1;
        }

        for(int j=1;arr[0]*j<=money;j++){
            dp[0][arr[0]*j]=1;
        }

        for(int i=1;i<arr.length;i++){
            for(int j=1;j<=money;j++){
                dp[i][j]=dp[i-1][j];
                dp[i][j]+=j-arr[i]>=0?dp[i][j-arr[i]]:0;
                dp[i][j]=dp[i][j]%100000007;
            }
        }

        return dp;

    }

  

 

原问题:
有n1+n2种面值的硬币,其中前n1种为普通币,可以取任意枚,后n2种为纪念币, 每种最多只能取一枚,每种硬币有一个面值,问能用多少种方法拼出m的面值?
 

 

 

 

 

 

 public static long moneyWays(int[] arbitrary,int[] onlyOne,int money){
        if(money<0){
            return 0;
        }

        if((arbitrary==null ||arbitrary.length==0)
                && (onlyOne==null || onlyOne.length==0)){
            return money==0?1:0;
        }
        //任意张的数组,一张的数组,不可能都没有
        long[][] dparb=getDpArb(arbitrary,money);
        long[][] dpone=getDpOne(onlyOne,money);

        if(dparb==null){
            return dpone[onlyOne.length-1][money];
        }
        if(dpone==null){
            return dparb[arbitrary.length-1][money];
        }

        long res=0;
        for(int i=0;i<=money;i++){
            res+=dparb[dparb.length-1][i] * dpone[dparb.length-1][money-i];
            res=res%1000000007;
        }

        return res;


    }

  

  

posted @ 2021-09-15 15:40  sherry001  阅读(56)  评论(0编辑  收藏  举报