找钱最少货币数

题目1
给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种货币都可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数。

dp[i][j] 表示前i中货币组成钱数j所用的最小货币数,那么dp[i][j] 等于 min(dp[i-1][j],
dp[i][j-array[i]]+1) dp[i-1][j] 就是前一个货币组成钱数j所用的最小货币数
dp[i][j-array[i]]+1 就是当前货币组成钱数j最小数

int minCoins1(vector<int>&v, int aim)
{
	if (v.size() == 0 || aim < 0)
	{
		return -1;
	}

	if (aim == 0)
		return 0;

	int n = v.size();
	int max_int = numeric_limits<int>::max();
	vector<vector<int>> dp(n, vector<int>(aim+1));
	for (int j = 1; j <= aim; j++)
	{
		dp[0][j] = max_int;
		if (j - v[0] >= 0 && dp[0][j - v[0]] != max_int)
		{
			dp[0][j] = dp[0][j - v[0]] + 1;
		}
	}

	int left = 0;
	for (int i = 1; i < n; i++)
	{
		for (int j = 1; j <= aim; j++)
		{
			left = max_int;
			if (j - v[i] >= 0 && dp[i][j - v[i]] != max_int)
			{
				left = dp[i][j - v[i]] + 1;
			}

			dp[i][j] = min(left, dp[i-1][j]);
		}
	}

	return dp[n - 1][aim] != max_int ? dp[n - 1][aim] : -1;
}

优化

 int minCoins2(vector<int>&v, int aim)
    {
    	if (v.size() == 0 || aim < 0)
    	{
    		return -1;
    	}

	if (aim == 0)
		return 0;

	int n = v.size();
	int max_int = numeric_limits<int>::max();
	vector<int> dp(aim + 1);
	for (int j = 1; j <= aim; j++)
	{
		dp[j] = max_int;
		if (j - v[0] >= 0 && dp[j - v[0]] != max_int)
		{
			dp[j] = dp[j - v[0]] + 1;
		}
	}

	int left = 0;
	int tmp = 0;
	for (int i = 1; i < n; i++)
	{
		for (int j = 1; j <= aim; j++)
		{
			left = max_int;
			tmp = dp[j];
			if (j - v[i] >= 0 && dp[j - v[i]] != max_int)
			{
				left = dp[j - v[i]] + 1;
			}

			dp[j] = min(left, tmp);
		}
	}

	return dp[aim] != max_int ? dp[aim] : -1;
}


int main()
{
	vector<int> v = { 2, 3, 5 };
	int n = minCoins1(v, 10);
	n = minCoins2(v, 10);
	return 0;
}

题目2
给定数组arr,arr中所有的值都为正数,且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求换钱有多少种方法。

暴力递归法

int process(std::vector<int>& arr, int index, int aim)
{
	int ret = 0;
	if (index == arr.size())
	{
		ret = 0 == aim ? 1 : 0;
	}
	else
	{
		for (int k = 0; k * arr[index] <= aim; ++k)
		{
			ret += process(arr, index + 1, aim - k * arr[index]);
		}
	}
	return ret;
}

int ExchangeMoney(std::vector<int>& arr, int aim)
{
	if (aim == 0)
	{
		return 1;
	}
	if (arr.size() == 0)
	{
		return 0;
	}
	return process(arr, 0, aim);
}

记忆搜索方法

int process1(std::vector<int>& arr, int index, int aim,
	std::map<std::pair<int, int>, int>& resultMap)
{
	int ret = 0;
	if (index == arr.size())
	{
		ret = 0 == aim ? 1 : 0;
	}
	else
	{
		for (int k = 0; k * arr[index] <= aim; ++k)
		{
			int nextIndex = index + 1;
			int nextAim = aim - k * arr[index];
			std::map<std::pair<int, int>, int>::iterator it = resultMap.find(std::make_pair(nextIndex, nextAim));
			if (it != resultMap.end())
			{
				ret += it->second;
			}
			else
			{
				ret += process1(arr, nextIndex, nextAim, resultMap);
			
		}
	}

	resultMap[make_pair(index, aim)] = ret;
}
return ret;
}

int ExchangeMoney1(std::vector<int>& arr, int aim)
{
	if (aim == 0)
	{
		return 1;
	}
	if (arr.size() == 0)
	{
		return 0;
	}
	std::map<std::pair<int, int>, int> resultMap;
	return process1(arr, 0, aim, resultMap);
}

int main()
{
	vector<int> v = { 2, 3, 5 };
	int n = ExchangeMoney1(v, 10);
	n = ExchangeMoney(v, 10);
	return 0;
}

动态规划法

int ExchangeMoney2(std::vector<int>& arr, int aim)
{
	if (aim == 0)
	{
		return 1;
	}
	if (arr.size() == 0)
	{
		return 0;
	}

	int n = arr.size();
	vector<vector<int>> dp(n, vector<int>(aim + 1));

	//dp[i][j]表示使用arr[0...i]组成j的方法数
	for (int i = 0; i<n; i++)
	{
		dp[i][0] = 1;//矩阵第一列,组成0的方法:1种
	}

	for (int j = 0; arr[0] * j <= aim; j++)
	{
		dp[0][arr[0] * j] = 1;//矩阵第一行,arr[0]组成arr[0]*j的方法:1种
	}

	//求一般位置的dp[i][j],由两者叠加
	for (int i = 1; i<n; i++)
	{
		for (int j = 1; j <= aim; j++)
		{
			dp[i][j] = dp[i - 1][j];//不用arr[i]组成j
			dp[i][j] += j - arr[i] >= 0 ? dp[i][j - arr[i]] : 0;//先用一张arr[i],剩下arr[0...i]组成j-arr[i]
		}
	}
	return dp[n - 1][aim];//arr[0...N]组成aim的方法数
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值