题目描述:给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示方法

思路:

一开始我想既然可以递归,那么加备忘或者自底向上DP就可以了,于是出现如下代码

 1 //有可能有重复计算,所以这个方法不可行
 2 int dp[10000];
 3 int fun(int n)
 4 {
 5     int i;
 6     dp[0] = 1;
 7     for(i = 1 ; i <= n ; ++i)
 8     {
 9         dp[i] = 0;
10         dp[1] = 1;
11         dp[5] = 2;
12         dp[10] = 5;
13         if(i >= 1)
14         {
15             dp[i] += dp[i-1]*dp[1];
16         }
17         if(i >= 5)
18         {
19             dp[i] += dp[i-5]*dp[5];
20         }
21         if(i >= 10)
22         {
23             dp[i] += dp[i-10]*dp[10];
24         }
25         if(i >= 25)
26         {
27             dp[i] += dp[i-25]*dp[25];
28         }
29     }
30     return dp[n];
31 }

然后再计算dp[10]的时候,发现,直接dp计算的话有很多重复计算。结果不正确。

于是就采用下面的递归算法。

 1 int fun_2(int n,int d)
 2 {
 3     int next_d;
 4     switch(d)
 5     {
 6     case 25:
 7         next_d = 10;
 8         break;
 9     case 10:
10         next_d = 5;
11         break;
12     case 5:
13         next_d = 1;
14         break;
15     case 1:
16         return 1;
17     }
18     int way = 0;
19     int i;
20     for(i = 0 ; i*d <= n ;++i)
21     {
22         way += fun_2(n-i*d,next_d);
23     }
24     return way;
25 }
26 
27 int main()
28 {
29     cout<<fun_2(100,25)<<endl;
30     return 0;
31 }

 其实具体思路也就是:对于n,采用1~i个25后,n-i*25用10、5和1分能表示的方法数;采用i个25和j个10后,n-i*25-j*10采用5和1分能表示的方法数;...依次递归即可