问题帖子:
http://topic.csdn.net/u/20081016/14/8E1B21C7-DBC8-40CE-BA93-28C4CAC4E461.html
用1元,2元,5元,10元,20元和50元的纸币组成100元,共有多少种情况。
要求写出除了多重循环方案之外的另一种程序代码,要求输出总方案数和每种方案中各纸币的个数。
这个是原题,当然,如果你有循环的实现也可以贴上来。
我的回复:
我来解一些,就用最简单的循环。。本来想再写个递归。。。大同小异,累死我了,忽略~
递归方法:(整体结构copy自91楼,并改进之)
-
- public static void Main()
- {
- int count = 0;
- // KEY-1:这里引入一些变量,减少递归次数
- int _CNY1Sum = 0, _CNY2Sum = 0, _CNY5Sum = 0;
- int _CNY10Sum = 0, _CNY20Sum = 0, _CNY50Sum = 0;
- int _CNY100Sum = 0;
- int Total = 0;
- int Limit = 100;
- Array<int> distribution[] = { 0, 0, 0, 0, 0, 0 };
- //1元组成的情况,最多有100种
- for (int a = 0; a <= 100; a++)
- {
- // initial each round
- ResetDistribution(distribution, distribution.size);
- // improvement
- Total = 0;
- _CNY1Sum = 1 * a;
- distribution[0] = a;
- Total = _CNY1Sum;
- if (Total == Limit) { count++; PrintSequence(distribution); continue; }
- if (Total > Limit) { break; }
- //2元的情况,最多有50种可能
- for (int b = 0; b <= 50; b++)
- {
- // improvement
- _CNY2Sum = 2 * b;
- distribution[1] = b;
- Total = _CNY2Sum + _CNY1Sum;
- if (Total == Limit) { count++; PrintSequence(distribution); break; }
- if (Total > Limit) { break; }
- //5元的情况,最多有20中可能
- for (int c = 0; c <= 20; c++)
- {
- // improvement
- _CNY5Sum = 5 * c;
- distribution[2] = c;
- Total = _CNY5Sum + _CNY2Sum + CNY1Sum;
- if (Total == Limit) { count++; PrintSequence(distribution); break; }
- if (Total > Limit) { break; }
- //10元的情况,最多10种可能
- for (int d = 0; d <= 10; d++)
- {
- // improvement
- _CNY10Sum = 10 * d;
- distribution[3] = d;
- Total = _CNY10Sum + _CNY5Sum + _CNY2Sum + _CNY1Sum;
- if (Total == Limit) { count++; PrintSequence(distribution); break; }
- if (Total > Limit) { break; }
- //20元的情况,最多5种可能
- for (int e = 0; e <= 5; e++)
- {
- // improvement
- _CNY20Sum = 20 * e;
- distribution[4] = e;
- Total = _CNY20Sum + _CNY10Sum + _CNY5Sum + _CNY2Sum + _CNY1Sum;
- if (Total == Limit) { count++; PrintSequence(distribution); break; }
- if (Total > Limit) { break; }
- //50元的情况,最多2种可能
- for (int f = 0; f <= 2; f++)
- {
- // improvement
- _CNY50Sum = 50 * f;
- distribution[5] = f;
- Total = _CNY50Sum + _CNY20Sum + _CNY10Sum + _CNY5Sum + _CNY2Sum + _CNY1Sum;
- if (Total == Limit) { count++; PrintSequence(distribution); break; }
- if (Total > Limit) { break; }
- }
- }
- }
- }
- }
- }
- Console.WriteLine("共有{0}种可能情况", count);
- }
- // new methods
- private void ResetDistribution(Array<int> myZone, int mySize)
- { for (int i = 0; i < mySize; i++) myZone[i] = 0 }
- private void PrintSequence(Array<int> myZone)
- {
- Console.WriteLine("Possible Solution for CNY100: " + myZone[0] + " "
- + myZone[1] + " " + myZone[2] + " " + myZone[3]
- + " " + myZone[3] + " " + myZone[4] + " " + myZone[5]);
- }
- /*
- * Author: Leemax Li
- * Created: 2008.11.05
- * MSN: leemax@live.com
- *
- */
------------------------------------ Analysis ------------------------------------
也许循环和递归不是最高效的方法,但却是最易懂的写法~而容易阅读的代码对于日后的维护也起着至关重要的作用。
也许有人可以写出更简洁的代码,但如果那简短高效代码却要花费别人1、2个小时去琢磨,那其实也不是什么好代码。
KISS: Keep It Simple Stupid~ remember?
但是,单纯的 100 × 50 × 20 × 10 ×5 × 2 粗暴的循环我个人觉得也是蛮“楞”的,所以综合计算机的高效和咱们人类的智慧,运用诸如统筹啊、线性规划啊(一堆乱七八糟的学术名词,用来提升自己的“表面”素质)等各种分析手段,我们可以发现:
当某次循环的等于或者大于限制时,接下来的循环就是完全没必要的。
也就是说:如果 99 张 1元钱了, 那么当我们进入 2 元钱的第一次循环时,不难发现 99 + 2 = 101 。。。这已经超出了题目要求,那必然两张2元钱或者再来一张5元钱更不可能符合范围要求,因此可以直接跳出循环(break)进入下一轮~
------------------------------------ Analysis ------------------------------------
- /*
- * 另一种思路:有一个经典的例子,大家一定都听说过:
- * 问,有人拿着1升的水桶,有人拿着2升的,有人拿着5、10、20升的,
- * 大家都排队接水,那么如何可以使得整体时间最短?答案是按照从小
- * 到大的顺序大家重新排队这样整体时间最短。(这是理想情况,但事
- * 实上人作为感情动物,是不可能会这样重新排队的)
- *
- * 因此,如果按照面值从【大】到【小】的顺序循环,那么除了完整的
- * 执行一次 100 × 50 × 20 × 10 ×5 × 2 的循环,找一些机制
- * (improvement)就可以使得时间进一步缩短。
- *
- * 注意:因为是从最内圈开始数的,所以“先接水”的人应该在最内圈。
- *
- * 91楼的代码是按照1.2.5.10.20.50的结构写的~但如果按照50.20.10.5.2.1
- * 的顺序循环,那么第一次的1元钱我们可以知道只需要循环15次(之前
- * 已经有85元钱了),第二次13次…………但这时判断终止条件又不一样了,
- * 需要新的条件,欢迎高手探讨一下。
- */
PS:如果是按照“接水”这种规划,似乎用点GoTo语句加上一些在for()括号外的++行为似乎可以达到这种效果?是不是会更接近AI?哈哈