/*---------------------------------------------------------------------------
File name: C76-75.cpp
Author: HJin
Date: 6/18/2007
6/18/2007
-----------------------------------------------------------
1. Refine the analysis: provide optimal substructure and the
recurrence formula for c[p].
C76-75.cpp --- the cpp source file for solving the 75th of the 76
classical C/C++ problems.
经典 76 道编程题 之 75:
75. (钱币系统问题) 某钱币系统由 k (k≤20) 种硬币组成, 币值依次为 a[1],
a[2],...,a[k], 其中 a[i] (i=1,2,...,k) 为互不相同的正整数, 且依降序排列,
a[1]≤200. 给定某整数币值 n(n≤3000), 要求用最少枚数的硬币表示这个币值.
输入: 用文件输入已知数据, 格式为:
第 1 行: k (硬币种数)
第 2 行: a[1] a[2] ... a[k] (各币值用空格隔开,已按降序排列好)
第 3 行: n (给定的币值)
输出: 直接在屏幕上输出结果. 如果该钱币系统无法表示币值 n,应输出'No',
否则按以下格式输出:
第 1 行: 最少钱币枚数 r.
第 2 行: 输出若干形如 m*n 的表达式, m 为币值, n为使用该币值的枚数.
各式第 2 个因子之和应等于 r, 各式乘积之和应等于 n.
例: 设 (a[1],a[2],a[3])=(5,2,1), n=12, 则应输出
3
5*2 2*1.
Analysis:
1. If 1 is in the set { a[1], a[2], ..., a[k] }, then we can change
any number n in at most n cions (since n = 1*n);
2. There is some set { a[1], a[2], ..., a[k] } such that this coin
changing problem can be solved by a greedy algorithm. For exmaple,
{5, 2, 1}
is such a set. And there exists a set { a[1], a[2], ..., a[k] }
such that the greedy algorithm does not give an optimal soln. For
example,
{5, 4, 1}
is such a set: 8 = 5*1 + 1*3 is the greedy soln, which needs 4 cions,
whereas 8 = 4*2 tells us that we can use just 2 coins.
3. A dynamic programming algorithm exists for any set. This needs some
understanding of the topic. Cormen's book is a wonderful reference.
4. This is really a classical and an interesting problem and a lot of
references exist. Just google for "coin changing problem, dynamic programming".
5. This problem needs more analysis than programming efforts. A complete
soln of this problem needs to describe (and prove) the optimal substructure,
which we omitted.
6. Our soln does NOT fulfil the requirements described in the problem
statement.
7. The optimal substructure:
Write n = m + (n-m). Then the left half of an optimal soln for n must
be an optimal soln for m, the right half of the optimal soln for n must
be an optimal soln for n-m.
8. Let c[p] be the minimum number of coins to make change for n, then
c[p] = 0, if p = 0;
= 1 + min { 1 + c[ p-a[i] ]: a[i] <= p}, if p> 0
Sample output:
n=0, No
n=1, No
n=2, No
n=3, No
n=4, No
n=5, No
n=6, No
n=7, r=1: 7*1
n=8, No
n=9, No
n=10, No
n=11, No
n=12, No
n=13, No
n=14, r=2: 7*2
n=15, r=1: 15*1
n=16, No
n=17, No
n=18, No
n=19, No
n=20, No
n=21, r=3: 7*3
n=22, r=2: 15*1 7*1
n=23, No
n=24, No
n=25, No
n=26, No
n=27, No
n=28, r=4: 7*4
n=29, r=3: 15*1 7*2
n=30, r=2: 15*2
n=31, r=1: 31*1
n=32, No
n=33, No
n=34, No
n=35, r=5: 7*5
n=36, r=4: 15*1 7*3
n=37, r=3: 15*2 7*1
n=38, r=2: 31*1 7*1
n=39, No
n=40, No
n=41, No
n=42, r=6: 7*6
n=43, r=5: 15*1 7*4
n=44, r=4: 15*2 7*2
n=45, r=3: 15*3
n=46, r=2: 15*1 31*1
n=47, No
n=48, No
n=49, r=7: 7*7
n=50, r=6: 15*1 7*5
n=51, r=5: 15*2 7*3
n=52, r=4: 15*3 7*1
n=53, r=3: 15*1 31*1 7*1
n=54, No
n=55, No
n=56, r=8: 7*8
n=57, r=7: 15*1 7*6
n=58, r=6: 15*2 7*4
n=59, r=5: 15*3 7*2
n=60, r=4: 15*4
n=61, r=3: 15*2 31*1
n=62, r=2: 31*2
n=63, r=9: 7*9
n=64, r=8: 15*1 7*7
n=65, r=7: 15*2 7*5
n=66, r=6: 15*3 7*3
n=67, r=5: 15*4 7*1
n=68, r=4: 15*2 31*1 7*1
n=69, r=3: 31*2 7*1
n=70, r=10: 7*10
n=71, r=9: 15*1 7*8
n=72, r=8: 15*2 7*6
n=73, r=7: 15*3 7*4
n=74, r=6: 15*4 7*2
n=75, r=5: 15*5
n=76, r=4: 15*3 31*1
n=77, r=3: 15*1 31*2
n=78, r=10: 15*1 7*9
n=79, r=9: 15*2 7*7
n=80, r=8: 15*3 7*5
n=81, r=7: 15*4 7*3
n=82, r=6: 15*5 7*1
n=83, r=5: 15*3 31*1 7*1
n=84, r=4: 15*1 31*2 7*1
n=85, r=11: 15*1 7*10
n=86, r=10: 15*2 7*8
n=87, r=9: 15*3 7*6
n=88, r=8: 15*4 7*4
n=89, r=7: 15*5 7*2
n=90, r=6: 15*6
n=91, r=5: 15*4 31*1
n=92, r=4: 15*2 31*2
n=93, r=3: 31*3
n=94, r=10: 15*3 7*7
n=95, r=9: 15*4 7*5
n=96, r=8: 15*5 7*3
n=97, r=7: 15*6 7*1
n=98, r=6: 15*4 31*1 7*1
n=99, r=5: 15*2 31*2 7*1
Press any key to continue . . .
Reference:
1. Thomas H. Cormen, introduction to algorithms.
2. 野比, 相当经典的76道编程自虐题分享.无答案 at
http://bbs.bc-cn.net/viewthread.php?tid=147853
*/
#include <iostream>
#define INFPOS ~(1<<31) // (2^31-1)
#define DIM(x) (sizeof( (x) ) / sizeof( (x)[0] ))
using namespace std;
/**
@param a --- the array { a[1], a[2], ..., a[k] }. It does not
need to in decreasing order.
@param k --- number of coins
@param n --- the target number we want to make change
@return --- returns the minimum (positive) number of coins needed
to make a successful change; or -1 if the money system
cannot make the change.
Space complexity is Theta(n+k) --- there is a version of dynamic
programming algorithm which uses Theta(n*k) space. Obviously,
our version beats that version.
Time complexity is Theta(n * k).
Note that our algorithm does not require the set to be sorted beforehand.
*/
int change(int *a, int k, int n, bool print= true);
/**
print the optimal soln.
*/
int makeChange(int *a, int k, int n, int* b, bool print);
int main(int argc, char** argv)
{
//int a[] = {5, 4, 3};
int a[] = {15, 31, 7};
int k = DIM(a);
int n;
int res;
// test #1
for(n=0; n<100; ++n)
change(a, k, n);
// test #2
//for(n=40; n<80; ++n)
//{
// res = change(a, k, n, false);
// if(res == -1)
// cout<<n<<endl;
//}
return 0;
}
int change(int *a, int k, int n, bool print)
{
/**
c --- c[i] stores soln for n = i. If there is no soln for n=i,
then stores INFPOS;
s --- s[i] stores the index of the first coin for n=i. If there is
no soln for n =i, then stores INFPOS;
b --- b[j] stores the number of cions for the soln.
*/
int* c = new int[n+1];
int* s = new int[n+1];
int* b = new int[k];
int coinIndex=INFPOS;
int n2 = n;
int min;
int i;
int p;
int temp;
for(i=0; i<k; ++i)
b[i] = 0;
c[0] = 0; // n=0 needs zero coins to make
s[0] = INFPOS; // no coins can be used for n = zero
for(p=1; p<=n; ++p) // for face value 1 to n
{
min = INFPOS; // +\inf
for(i=0; i<k; ++i) // loop over the set of coins
{
if(a[i] <= p)
{
// temp implements the relation: 1 + +\inf = +\inf
temp = (c[p-a[i]] == INFPOS ? INFPOS : 1 + c[p-a[i]]);
if (min > temp )
{
min = temp; // keeps the local minimum
coinIndex = i; // select coin i
}
}
}
c[p] = min; // keeps the global min (the soln for n=p)
s[p] = coinIndex;
}
/**
build number of cions for each a[i] and store it in b[i]
*/
while(n2>0)
{
if( s[n2]>=0 && s[n2] <k )
++b[s[n2]];
n2 -= a[s[n2]];
}
// reuse n2 to keep a value
n2 = makeChange(a, k, n, b, print);
delete [] c;
delete [] s;
delete [] b;
return n2;
}
int makeChange(int *a, int k, int n, int* b, bool print)
{
int i;
int r=0;
int sum = 0;
if(print)
cout<<"n="<<n;
/**
calculate r --- the total number of cions for n
*/
for(i=0; i<k; ++i)
{
r+= b[i];
sum += b[i] * a[i];
}
/**
If the sum is not n, then there are no solns.
And treat the case n=0 as there are no solns.
*/
if (sum != n || n<1)
{
if(print)
cout<<", No"<<endl;
return -1;
}
if(print)
cout<<", r="<<r<<":\t";
for(i=0; i<k; ++i)
{
if( b[i] > 0 )
{
if(print)
cout<<a[i]<<"*"<<b[i]<<" ";
}
}
if(print)
cout<<endl;
return r;
}
[此贴子已经被作者于2007-6-19 3:01:51编辑过]