錢幣組合問題(二):(每種硬幣次數受限)
阿新 • • 發佈:2019-01-27
Description
設有n種不同的錢幣各若干,可用這n種錢幣產生許多不同的面值。
如給定面值7分,有1分3張,2分3張,5分1張,能組成給定面值7分的方法有如下4種:
3個1分+2個2分;
1個1分+3個2分;
2個1分+1個5分;
1個2分+1個5分。
你的程式設計任務:給定面值m,和n種不同錢幣及其張數,求給定面值m能有多少種不同的構成方法數。
Input
第1行有1個正整數n(1<=n<=10),表示有n種不同的錢幣。
第2行有n個數,分別表示每種錢幣的面值v[1]...v[n](0<=v[i]<=100,1<=i<=n)。
第3行有n個數,分別表示每種錢幣的張數k[1]...k[n](0<=k[i]<=100,1<=i<=n)。
第4行有1個數,表示給定的面值m (1<=m<=20000)。
Output
計算出給定面值不同的方法種數
Sample Input
3
1 2 5
3 3 1
7
Sample Output
4
思路:
與前篇文章思路類似,增加判斷條件即可
思路一:暴力窮舉
超時,太長了,不愛寫了,可以參考前一篇文章
思路二:深度優先遍歷,分支限界,n過大時會超時,思路同前一篇文章,限界條件為當前金額大於要求金額或者當前使用數目大於提供數量
static int dc = 0; static int[] sum = new int[10000]; /** * * @param count 當前總值 * @param location 當前下標 * @param n 題幹中總面額 * @param coins 硬幣型別 * @param k 硬幣個數 */ public static void dfs(int count,int location,int n,int[] coins,int[] k){ if((count==n)&&sum[location]<=k[location]){ dc++; for(int i =0;i<coins.length;i++){ //System.out.print(sum[i]); } //System.out.println(); }else if((count>n)||(sum[location]>k[location])){ return; }else{ for(int i=location;i<coins.length;i++){ sum[i]++; dfs(count+coins[i], i, n, coins, k); sum[i]--; } } }
思路三:動態規劃,與個數不受限的思路類似,只不過在初始化時,需要注意:最小面額不一定是“1”,所以要判斷能否只用最小面額表示、且最小面額足夠使用;如果要求金額是0,那麼一定有一種方案(即什麼也不做)。我的程式裡把面值為“0”頁看成了一種貨幣,所以分配空間時多分配了1,d[i][j] +=d[i-1][j-k*coins[i-1]];i<=coins.length。(其中d[i][j]表示組成j面額時只是用前i種貨幣可以組成的方式數目)
當然這個過程是一個累加的過程,二維陣列邏輯好理解,也可以用一位陣列表示,更簡單/** * * @param coins 硬幣種類 * @param c 每種硬幣個數 * @param n 題目中要求的金額 */ public static void dynamic_prommgram(int coins[], int c[], int n){ if(coins.length!=c.length) return; int[][] d = new int[coins.length+1][n+1]; for(int i = 0;i<coins.length;i++) d[i][0] = 1; for(int j =0;j<=n;j++) { if((j%coins[0]==0)&&(j/coins[0]<=c[0])){ //只用第一種錢幣就可以表示,並且數量夠用 d[1][j]=1; }else{ d[1][j]=0; } } for(int i = 2;i<=coins.length;i++){//每種硬幣 for(int j = 1;j<=n;j++){//每種金額 for(int k = 0;k<=c[i-1];k++){//因為邏輯上把幣值為0也算作了一種貨幣,所以此處為i-1 //d[i,j] = d[i-1,j] + d[i-1,j-1*v[i]] + d[i-1,j-2*v[i]] + ... + d[i-1,j-k[i]*v[i]] if(k*coins[i-1]>j) break; d[i][j] +=d[i-1][j-k*coins[i-1]]; } } } System.out.println(d[coins.length][n]); }
/**
* 一維陣列
* @param n 種類數
* @param v 幣值
* @param k 張數
* @param m 面值
* @return
*/
public static int solve(int n, int v[], int k[], int m)
{
int[] dp = new int[m+10];//d[i][0]=1
for(int j=0; j<=m; j++)
{
dp[j] = (j%v[0]==0 && j/v[0]<=k[0]) ? 1 : 0;//d[1][j]
}
for(int i=1; i<n; i++)//d[2][] -d[n-1][]
{
for(int j=m; j>0; j--)
{
for(int u=1; u<=k[i]; u++)
{
if((j-u*v[i]) < 0) break;
dp[j] += dp[j-u*v[i]];
}
}
}
return dp[m];
}
個人傾向於二維陣列,因為劉玉貴老師上課時講的作業題一般都是用二維陣列,習慣了(*^__^*) 嘻嘻……
參考資料:
http://blog.csdn.net/u013300579/article/details/78081458
http://createfile.blog.163.com/blog/static/2125953042012101725459765/