这是一道ACM之上的题目,在网上看到的大多是用贪心算法完成的,而贪心策略就是大面额的钱币尽可能的取,这样貌似没什么问题,比如15元,按照贪心策略来说尽可能的去面额的的及一张10+一张5。确实这些情况下,该贪心策略是没问题的,但是若是11呢,在这种情况下,按照贪心策略,就是取一张10,但是问题来了,没有1元的,就没有答案,而事实是取一张五元+三张2元的,这是最优的选择,所以对于该问题贪心策略是不可行的(原因我大概懂一点,但是说不清楚)。
那现在再来考虑这个问题,现在记d(i)=j:及最少需要j张钱币才可以凑齐i元,
那么不难知
d(0)=0;d(1)=INF;d(2)=d(5)=d(10)=d(20)=d(50)=d(100)=1;
记d(i)=INF,i<0;其中INF代表无穷大;
那么其余i,d(i)=min(d(i-2),d(i-5),d(i-10),d(i-20),d(i-50),d(i-100))+1;
这样偶们就可以通过递推得到最后的解,这个再算法里应该叫做DP(动态规划)。
关键代码片段:
public void SequentialDerivation(int n){
a=new int[n+1];
for(int i=1;i<=n;i++){
if(i==2||i==5||i==10||i==20||i==50||i==100) a[i]=1;
else if(i<2) a[i]=TestCion.INFINITE;
else if(i<5) a[i]=a[i-2]+1;
else if(i<10) a[i]=Math.min(a[i-2], a[i-5])+1;
else if(i<20) a[i]=Math.min(Math.min(a[i-2], a[i-5]), a[i-10])+1;
else if(i<50) a[i]=Math.min(Math.min(a[i-2], a[i-5]), Math.min(a[i-10], a[i-20]))+1;
else if(i<100) a[i]=TestCion.min(Math.min(a[i-2], a[i-5]), Math.min(a[i-10], a[i-20]),a[i-50])+1;
else a[i]=TestCion.min(Math.min(a[i-2], a[i-5]), Math.min(a[i-10], a[i-20]),Math.min(a[i-50], a[i-100]))+1;
}
for (int i = 1; i < a.length; i++) {
if((i-1)%10==0) System.out.println();
if(a[i]<TestCion.INFINITE)
System.out.print(String.format("%-10d", a[i]));
else
System.out.print(String.format("%-10s", "No Answer"));
}
}
public static int min(int a,int b,int c){
//求三个数的最小值
a=a<b?a:b;
return a<c?a:c;
}
但是这样不难发现,若是问题上万上亿,那么开辟的数组也就足够浪费空间,那是不是有别的什么好的方法来解决这道题???
下面我可能说得很乱,因为我也不知道该怎么说清楚。
先说第一点,首先我们注意到,这道题和我们现实生活中的钱币的划分很相似,但是现实中还多一个1元的,我们知道这样的话我们可以划分所有的>1的钱币,而且一开始讲的贪心策略是适用的,
其次我们注意到在这种钱币的划分中只有5是奇数,其他均为偶数,这就导致对于i若为奇数,则必有一张5,若为偶数,则必定没有5,因为若有5,则必定是偶数个5,而没有两个5就可化为一个10,所以有偶数个5的绝对不是最优的。
然后我乱想一通又得到两个算法:
一个太复杂,不写;
另一个:
public int greedyAlgorithm(int n){
int total=0;
if(n==1||n==3)
return INFINITE;
if(n%2==1)
total++;
total+=n/100;n%=100;
total+=n/50;n%=50;
total+=n/20;n%=20;
total+=n/10;n%=10;
total+=n/2;n%=2;
return total;
}
这个是但心算法,先对奇数处理,若为奇数,必有一张5,即total++;
最后我做一个大胆的假设:
若是最小的奇数存在时,则对奇数的钱币时可以采用贪心策略;(待验证)
若是最小的偶数存在时,则对偶数的钱币时可以采用贪心策略.(待验证)