///
//
// 2007-07-11
// By Rappizit@yahoo.com.cn
//
// 问题描述:
// 若有人民币 1 元 3 张, 2 元 2 张, 5 元 1 张, 10 元 1 张, 50 元 1 张,问能组成多少种币值?各有多少种方案?
//
// 解法描述:
// 根据给定的各种人民币的面值和数目,构造母函数
// F(x) = (1 + x + x^2 + x^3) (1 + x^2 + x^4) (1 + x^5) (1 + x^10) (1 + x^50)
// 展开该母函数,则对于项 x^k , k 为组成的币值,其系数为组成该币值的方案数。例如, 3x^57 表示组成 57 元币值有
// 3 种方案: 50 + 5 + 2 ; 50 + 5 + 1 + 1 ; 50 + 2 + 2 + 1 + 1 + 1 。
//
///
#include < iostream >
using namespace std;
#define MAX 100 // 最多有 MAX 种不同面值的人民币
void CaseOfValue ( int Value [], int Count [], int Size, int Result [], int Length)
... {
int i, j, k, n1, n2, Switch;
int* Poly [2];
Poly [0] = new int [Length]; // Poly [Switch] [] 保存被乘(多项)式的系数
Poly [1] = new int [Length]; // Poly [1 - Switch] []保存积(多项)式的系数
// 将 1 作为初始的被乘(多项)式,系数保存于 Poly [0] []
Poly [0] [0] = 1;
for (i = 1; i < Length; i ++)
...{
Poly [0] [i] = 0;
}
Switch = 0;
n1 = 0; // n1 为被乘(多项)式的次数
// 迭代地计算积(多项)式
for (i = 0; i < Size; i ++)
...{
n2 = n1 + Value [i] * Count [i]; // n2 为积(多项)式的次数
// 积(多项)式初始化为 0
for (j = 0; j <= n2; j ++)
...{
Poly [1 - Switch] [j] = 0;
}
// 用乘(多项)式的各项(高次到低次)分别乘以被乘(多项)式的各项(高次到低次)
// 然后累加到积(多项)式
for (j = Value [i] * Count [i]; j >= 0; j -= Value [i])
...{
for (k = n1; k >= 0; k --)
...{
Poly [1 - Switch] [k + j] += Poly [Switch] [k];
}
}
n1 = n2;
Switch = 1 - Switch;
}
// 将最终的结果多项式的系数复制到 Result []
for (i = 0; i < Length; i ++)
...{
Result [i] = Poly [Switch] [i];
}
delete Poly [0];
delete Poly [1];
return;
}
void main ()
... {
int i, cnt = 0, Size = 0, Length = 0;
int Value [MAX], Count [MAX], *Result;
while (Size <= 0)
...{
cin >> Size; // 输入人民币种类数目
}
for (i = 0; i < Size; i ++)
...{
cin >> Value [i] >> Count [i]; // 输入每种人民币的面值及数目
Length += Value [i] * Count [i];
}
Length ++;
Result = new int [Length];
CaseOfValue (Value, Count, Size, Result, Length);
cout << "Value Case ";
for (i = 0; i < Length; i ++)
...{
if (Result [i])
...{
cout << i << ' ' << Result [i] << ' ';
cnt ++;
}
}
cout << "Value Count : " << cnt << endl;
delete Result;
}
//
// 测试数据:
// 3
// 1 3
// 2 1
// 3 1
//
// 测试结果:
// Value Case
// 0 1
// 1 1
// 2 2
// 3 3
// 4 2
// 5 3
// 6 2
// 7 1
// 8 1
// Value Count : 9
//
// 事实上,将整数 k 不限制地拆分,可输入 k 种人民币的面值 Value [1..k] 及数目 Count [1..k],
// 对于 1 <= i <= k , Count [i] = (int)(Value [i] / k) ,则从测试结果的 Value 一栏找到 k ,
// 对应的 Case 数目即为拆分方法数。
// 当要求 k 拆分成的数有限制,或全为奇数,或者全为偶数,或者有最大限制,那么适当改变人民币的面值和数目即可。
// 但是此程序不能求出拆分方式。
//
///