首页 > ACM题库 > UVA > UVa-147-Dollars(钱币)-[组合数学]
2014
01-22

UVa-147-Dollars(钱币)-[组合数学]

Time limit: 3.000 seconds
限时:3.000秒

 

Problem
问题

New Zealand currency consists of $100, $50, $20, $10, and $5 notes and $2, $1, 50c, 20c, 10c and 5c coins. Write a program that will determine, for any given amount, in how many ways that amount may be made up. Changing the order of listing does not increase the count. Thus 20c may be made up in 4 ways: 1 × 20c, 2 × 10c, 10c+2 × 5c, and 4 × 5c.
新西兰的流通货币中,纸币分为五种:100元、50元、20元、10元和5元;硬币分为六种:2元、1元、50分(即五毛)、20分、10分和5分。写一个程序计算出对于给定的任意金额,可以有多少种不同的钱币组合方式。仅仅改变顺序的不能算。比如20分可以有以下4种组合方式:1 × 20分, 2 × 10分, 10分+2 × 5分, 和4 × 5分。

 

Input
输入

Input will consist of a series of real numbers no greater than $300.00 each on a separate line. Each amount will be valid, that is will be a multiple of 5c. The file will be terminated by a line containing zero (0.00).
输入由一组实数构成,每个实数都不会超过300.00元,并且都独占一行。金额都是有效的,即都是5分的整数倍。输入由一行零元(0.00)表示结束。

 

Output
输出

Output will consist of a line for each of the amounts in the input, each line consisting of the amount of money (with two decimal places and right justified in a field of width 6), followed by the number of ways in which that amount may be made up, right justified in a field of width 17.
每行输入对应一行输出,包括总金额(保留两位小数并右对齐至第6列),后面是可以组成该金额的方法数,右对齐宽度为17。

 

Sample Input
输入示例

0.20
2.00
0.00

 

Sample Output
输出示例

  0.20                4
  2.00              293

 

Analysis
分析

此题较难,是一个子集合问题。可以用递归式直接计算任意给定金额的组合方案,但是所给的数据可能非常大,用递归的话肯定会TLE,因此必须通过递推的方式生成所有可能金额的答案,运行时直接查表即可。

建表过程如下:首先任何金额都可仅由5分组成这1种方案,如果金额刚好等于某一种钱币,就还有这一种方案,因此10分有2种方案。15分的第一种方案可仅由5分组成,要计算由10分或5分组成(不包括前面算过的全部5分的情况)的情况,可先减掉10分(因为这种情况至少包括一个10分),剩下的5分就只有1种情况,因此共有2种方案。20分的第一种方案可由5分组成;第二种情况先减掉10分还剩10分,而10分由10分或5分组成的情况有2种,因此都要计入;最后一种方案就是直接使用20分,因此一共4种方案。其余的以此类推。

注意到所有钱数都是5的整数倍,因此可以将货币面值和查询的金额统一除以5再计算。要注意按指定的格式输出结果,并在输入0时退出程序。

这个思路讲起来很麻烦,如果您有更好的公式化理论敬请提出!

 

Solution
解答

 

#include <iomanip>
#include <iostream>
using namespace std;
int main(void) {
	//钱币数,全部除以5
	int Coins[] = {1, 2, 4, 10, 20, 40, 100, 200, 400, 1000, 2000};
	long long Tbl[6001] = {1}; //各金额组合方案数总表
	//以下开始建表,按钱币种类进行循环
	for (int i = 0; i < 11; i++){
		//仅使用i之前的钱币进行组合,但不包括不使用i的情况
		for (int j = Coins[i]; j < 6001; j++) {
			//先减掉第i种钱币,然后加上剩下的金额的组合方案数
			Tbl[j] += Tbl[j - Coins[i]];
		}
	} //以下为设定格式,查表输出结果。
	cout << fixed << showpoint << setprecision(2);
	for (float fIn; cin >> fIn && fIn != 0; cout << endl) {
		cout << setw(6) << fIn << setw(17) << Tbl[(int)(fIn * 20 + 0.5f)];
	}
	return 0;
}

转自:http://www.cnblogs.com/devymex/archive/2010/08/27/1808714.html


  1. 不同的编译器,printf(),执行的顺序不同,所以结果也就不同,建议不要那么写