硬币拼凑(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; }