题目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的方法数
}