背包问题

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]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值