题目详情
硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
示例1:
输入: n = 5
输出:2
解释: 有两种方式可以凑成总金额:
5=5
5=1+1+1+1+1
示例2:
输入: n = 10
输出:4
解释: 有四种方式可以凑成总金额:
10=10
10=5+5
10=5+1+1+1+1+1
10=1+1+1+1+1+1+1+1+1+1
说明:
注意:
你可以假设:
- 0 <= n (总金额) <= 1000000
——题目难度:中等
思路(借鉴)
n25、n10、n5、n1分别代表币值为25、10、5、1分的硬币数
n= 25*n25 + 10*n10 + 5*n5 + 1*n1
1.一开始三层嵌套for循环试试能不能暴力穷举解决,不是四层for循环的原因是因为题目只要求返回有几种表示法,所以n5一旦确认下来,n1的数量也能够确定。第一次结果当然是超时了...class Solution { public: const int M = 1000000007; int waysToChange(int n) { // n= 25*n25 + 10*n10 + 5*n5 + 1*n1 int ans = 0; for(int n25=0;n25<=n/25;n25++) //n25、n10、n5分别代表币值为25、10、5分的硬币数 { int temp1 = n - 25*n25; for(int n10=0;n10<=temp1/10;n10++) { int temp2 = temp1 - 10*n10; for(int n5=0;n5<=temp2/5;n5++) { ans++; //5分的硬币数量确定下来了,1分的硬币数量也就确认下来了 ans=ans%M; } } } return ans; } };
第一次结果
2.接着发现n5循环的次数其实和n10的大小有关,所以进一步化简。但最后还是会超时。class Solution { public: const int M = 1000000007; int waysToChange(int n) { // n= 25*n25 + 10*n10 + 5*n5 + 1*n1 int ans = 0; for(int n25=0;n25<=n/25;n25++) { int temp1 = n - 25*n25; for(int n10=0;n10<=temp1/10;n10++) { ans = ans + (temp1 - 10*n10)/5 + 1;// 每次不同的n10都有( (temp1 - 10*n10)/5 + 1(当n5=0必有一种) )种 ans = ans%M; } } return ans; } };
第二次结果
3.最后其实关于n10的这层for循环也可以被n25来决定,这时可以将第二次尝试中第二层for循环的"ans = ans + (temp1 - 10*n10)/5 + 1"看成是看成等差数列求和这一过程(下面把temp1看成temp)
通项公式an = (temp - n10*10)/5 + 1
项数为 temp/10 + 1 项首项为 temp/5 + 1 末项为 (temp - temp/10*10)/5 + 1 1.当temp=10*k(k>=0)时, 末项为0 2.当temp=10k(k>=1) - m(m<10),末项为10k-m-10*((10k-m)/10)
例:k=2,m=1时, temp=19,末项为2
所以末项也可以表示为 (temp%10)/5 + 1
了解了思路,代码如下
class Solution {
public:
const int M = 1000000007;
int waysToChange(int n) { // n= 25*n25 + 10*n10 + 5*n5 + 1*n1
int ans = 0;
for(int n25=0;n25<=n/25;n25++)
{ int temp = n - 25*n25; //等效为等差数列的首项
ans=(ans + ( (temp/5 + 1)+(long long)((temp%10)/5 + 1) )*(temp/10+1)/2 )%M;
}
return ans;
}
};
结果