给出大于或等于给定数字的最小总和的子集
subset which gives the least sum greater than or equal to a given number
我有一组(多个)正数,例如。 {71.28, 82.62, 148.77, 85.05, 50.76, 103.41}
.
我想找到总和大于或等于给定数字的子集。
例如。如果最小值是 270
,则结果将是 {148.77, 71.28, 50.76}
,总和为 270.81
。
注意:我假设解决方案可能更类似于背包而不是子集总和。
子集和问题和背包问题的解很相似,你可以用其中一个来解决问题。然而,背包问题有一个动态规划解决方案,它更有利于解决这个特定问题。看看这个 link 看看我的出发点:
http://www.geeksforgeeks.org/dynamic-programming-set-10-0-1-knapsack-problem/
上述解决方案递归地迭代集合的每个排列,从起始总和中减去每个集合元素的值,直到减法导致总和值为负。这表示检查的子集的值大于指定输入数字的情况,或者在您的示例中,子集的附加值大于 270 的情况。在 DP 背包解决方案中,我们只是跳过该集合元素并移动到下一个。在我的解决方案中,我检查该解决方案的值是否是迄今为止看到的大于输入数字(在您的示例中为 270)的最小值。如果是,我更新函数的两个参数。一个论点是跟踪总和与我们正在检查的集合中的元素之间的差异。这个论点给了我们子集的附加值与输入数字的接近度,而无需计算附加值或记住原始输入数字。另一个参数是当前集合,其相加值最接近但大于我们的输入数字。在 C++ 中,该集合保存在 std::vector 引用中(它也可以使用集合或多重集合)。如果没有添加到大于输入数字的值的集合,则此算法 returns 一个空向量。
#include<iostream>
#include<vector>
#include<climits>
template<typename T>
void print(std::vector<T> v)
{
for(auto i : v)
std::cout<<i<<" ";
std::cout<<std::endl;
}
template<typename T>
T closestVal(T sum, std::vector<T>& set, size_t n, std::vector<T> curSet, int& minSum, std::vector<T>& ret)
{
if(n == 0 || sum == 0)
return 0;
if(set[n-1] >= sum) {
if(sum-set[n-1] > minSum) {
minSum = sum-set[n-1];
std::vector<T> newSet = curSet;
newSet.push_back(set[n-1]);
ret = newSet;
}
return closestVal(sum, set, n-1, curSet, minSum, ret);
}
else {
std::vector<T> newSet = curSet;
newSet.push_back(set[n-1]);
return std::max(
set[n-1] + closestVal(sum-set[n-1],set,n-1, newSet, minSum, ret),
closestVal(sum, set, n-1, curSet, minSum, ret)
);
}
}
int main()
{
std::vector<double> ms{71.28, 82.62,148.77, 85.05, 50.76, 103.41};
std::vector<double> ret; //ret is empty, will be filled with the return value
int i = INT_MIN; //i is instantiated to the smallest possible number
closestVal(270.81, ms, ms.size(), {}, i, ret);
print(ret);
return 0;
}
我有一组(多个)正数,例如。 {71.28, 82.62, 148.77, 85.05, 50.76, 103.41}
.
我想找到总和大于或等于给定数字的子集。
例如。如果最小值是 270
,则结果将是 {148.77, 71.28, 50.76}
,总和为 270.81
。
注意:我假设解决方案可能更类似于背包而不是子集总和。
子集和问题和背包问题的解很相似,你可以用其中一个来解决问题。然而,背包问题有一个动态规划解决方案,它更有利于解决这个特定问题。看看这个 link 看看我的出发点: http://www.geeksforgeeks.org/dynamic-programming-set-10-0-1-knapsack-problem/ 上述解决方案递归地迭代集合的每个排列,从起始总和中减去每个集合元素的值,直到减法导致总和值为负。这表示检查的子集的值大于指定输入数字的情况,或者在您的示例中,子集的附加值大于 270 的情况。在 DP 背包解决方案中,我们只是跳过该集合元素并移动到下一个。在我的解决方案中,我检查该解决方案的值是否是迄今为止看到的大于输入数字(在您的示例中为 270)的最小值。如果是,我更新函数的两个参数。一个论点是跟踪总和与我们正在检查的集合中的元素之间的差异。这个论点给了我们子集的附加值与输入数字的接近度,而无需计算附加值或记住原始输入数字。另一个参数是当前集合,其相加值最接近但大于我们的输入数字。在 C++ 中,该集合保存在 std::vector 引用中(它也可以使用集合或多重集合)。如果没有添加到大于输入数字的值的集合,则此算法 returns 一个空向量。
#include<iostream>
#include<vector>
#include<climits>
template<typename T>
void print(std::vector<T> v)
{
for(auto i : v)
std::cout<<i<<" ";
std::cout<<std::endl;
}
template<typename T>
T closestVal(T sum, std::vector<T>& set, size_t n, std::vector<T> curSet, int& minSum, std::vector<T>& ret)
{
if(n == 0 || sum == 0)
return 0;
if(set[n-1] >= sum) {
if(sum-set[n-1] > minSum) {
minSum = sum-set[n-1];
std::vector<T> newSet = curSet;
newSet.push_back(set[n-1]);
ret = newSet;
}
return closestVal(sum, set, n-1, curSet, minSum, ret);
}
else {
std::vector<T> newSet = curSet;
newSet.push_back(set[n-1]);
return std::max(
set[n-1] + closestVal(sum-set[n-1],set,n-1, newSet, minSum, ret),
closestVal(sum, set, n-1, curSet, minSum, ret)
);
}
}
int main()
{
std::vector<double> ms{71.28, 82.62,148.77, 85.05, 50.76, 103.41};
std::vector<double> ret; //ret is empty, will be filled with the return value
int i = INT_MIN; //i is instantiated to the smallest possible number
closestVal(270.81, ms, ms.size(), {}, i, ret);
print(ret);
return 0;
}