1. 找零钱问题
某个国家一共发行了a1, a2, ..., ak种不同面额的钞票,为了方便起见,假设a1 < a2 < ... < ak,现在手上有n,请问要如何把n兑换成a1, a2, ..., ak的这些钞票,使得所有钞票的量为最少。
这道题用递归的方法来求解不能难:
#include <iostream>
using namespace std;
#define MAXSIZE 100
#define min(a, b) ((a) <= (b) ? (a) : (b))
int base[] = {1, 3, 4};
int k = sizeof base / sizeof *base;
int getMinMoney(int money)
{
if (money < 0)
return k;
else if (money == 0)
return 0;
int minValue = k;
for (int i = 0; i < k; i++)
{
minValue = min(getMinMoney(money - base[i]) + 1, minValue);
}
return minValue;
}
void main()
{
int result = getMinMoney(10);
cout << "result = " << result << endl;
}
可以看到getMinMoney存在重复求解的现象,那么下面的代码就是利用动态规划来求解的
#include <iostream>
using namespace std;
#define MAXSIZE 100
#define min(a, b) ((a) <= (b) ? (a) : (b))
void main()
{
int money[MAXSIZE + 1];
int base[] = {1, 3, 4};
int k = sizeof base / sizeof *base;
int n;
int i, j, MIN;
char line[100];
printf("\nMinimum Money Change Program");
printf("\n========================\n");
printf("\n\nBase Values: ");
for (i = 0; i < k; i++)
printf("%d ", base[i]);
printf("\n\nYour input please --> ");
cin >> n;
money[0] = 0;
money[1] = 1;
for (i = 2; i <= n; i++)
{
MIN = n;
for (j = 0; j < k; j++)
{
if (i >= base[j])
MIN = min(money[i - base[j]] + 1, MIN);
money[i] = MIN;
}
}
printf("\n\nMinimum = %d", money[n]);
}
递归的动态规划代码也将不难写出~
#include <iostream>
using namespace std;
#define MAXSIZE 100
#define min(a, b) ((a) <= (b) ? (a) : (b))
int base[] = {1, 3, 4};
int k = sizeof base / sizeof *base;
int getMinMoney(int money, int *counts)
{
if (money < 0)
return k;
else if (money == 0)
return 0;
if (counts[money] != 65535)
return counts[money];
int minValue = k;
for (int i = 0; i < k; i++)
{
minValue = min(getMinMoney(money - base[i], counts) + 1, minValue);
}
counts[money] = minValue;
return minValue;
}
void main()
{
int money = 10;
int *counts = new int[11];
for (int i = 1; i <= money; i++)
counts[i] = 65535;
int result = getMinMoney(money, counts);
cout << "result = " << result << endl;
delete[] counts;
}
扩充这个程序,让它至少可以报告出一组张数为最少的钞票面额!
void getMoneys(int money, int *counts, vector<int> &ivec)
{
int theMoney = money;
while (theMoney != 0)
{
int minValue = money;
int candidate;
for (int i = 0; i < k; i++)
{
minValue = min(counts[theMoney - base[i]] + 1, minValue);
if (minValue == counts[theMoney - base[i]] + 1)
candidate = base[i];
}
ivec.push_back(candidate);
theMoney = theMoney - candidate;
}
}
如果有n个对象,它们的计量单位各为k1, k2, ..., kn,现在有一个可以容纳k单位的背包,请写一个程序,找出有没有办法在1、2、...、n中选出p个元素,使得计量单位的总和为K,因此可以刚好把背包装满,这就是著名的背包问题。
背包为题的递归算法是建立一个bool型的数组,如果选取第k个对象,则将bool[k]置为true,否则将其置为false,那么循环求解算法呢。。。
参考书上的样例代码如下:
#include <iostream>
using namespace std;
#define YES 1
#define NO 0
#define NOT_FOUND -1
void reverse(int[], int);
int knapsack(int *size, int n, int SIZE, int *result)
{
int **exist;
int **member;
int count;
int i, j;
exist = (int **)malloc(sizeof(int *) * (n + 1));
exist[0] = (int *)malloc(sizeof(int) * (n + 1) * (SIZE + 1));
member = (int **)malloc(sizeof(int *) * (n + 1));
member[0] = (int *)malloc(sizeof(int) * (n + 1) * (SIZE + 1));
for (i = 1; i <= n; i++)
{
exist[i] = exist[i - 1] + (SIZE + 1);
member[i] = member[i - 1] + (SIZE + 1);
}
exist[0][0] = YES;
for (j = 1; j <= SIZE; j++)
exist[0][j] = NO;
for (i = 1; i <= n; i++)
{
for (j = 0; j <= SIZE; j++)
{
exist[i][j] = member[i][j] = NO;
if (exist[i - 1][j])
{
exist[i][j] = YES;
member[i][j] = NO;
}
else if (j >= size[i - 1])
{
if (exist[i - 1][j - size[i - 1])
{
exist[i][j] = YES;
member[i][j] = YES;
}
}
}
}
if (exist[n][SIZE])
{
for (count = 0, i = n, j = SIZE; i != 0 && j != 0)
{
if (member[i][j] == YES)
{
result[count++] = --i;
j -= size[i];
}
else
i--;
}
reverse(result, count);
}
else
count = NOT_FOUND;
free(exist[0]);
free(member[0]);
return count;
}
#define SWAP(a, b) { temp = a; a = b; b = temp; }
void reverse(int *x, int n)
{
int i, j, temp;
for (i = 0, j = n - 1; i < j; i++, j--)
SWAP(x[i], x[j]);
}