假设您有一个低于10的数字列表 - 例如{7,6,5,4} 目标是将它们组合成总和> = 10并且我想要实现最大数量的组合而不重用任何数字。此外,如果可能,解决方案应尽可能接近10。
E.g。
7 + 4& 6 + 5 = 2个解(11& 11-到10的最大距离是1)
7 + 5& 6 + 4 = 2个解(12& 10-到10的最大距离是2)
7 + 6 = 1溶液(因为剩余的5 + 4 <10)
我的第一种方法是取最高数字,然后加上最小数字。如果这不等于或超过限制,那么我会添加下一个最低数字 它导致了这段代码:
IList<int> numbers = new List<int>{6, 4, 2, 2, 5, 2};
var numbersOrdered = numbers.OrderByDescending(x => x).ToList();
var resultList= new List<List<int>>();
int highIndex = 0;
int lowIndex = numbersOrdered.Count() - 1;
List<int> result = new List<int>();
result.Add(numbersOrdered[highIndex]);
int sum = numbersOrdered[highIndex];
while (highIndex < lowIndex)
{
sum += numbersOrdered[lowIndex];
result.Add(numbersOrdered[lowIndex]);
if (sum >= 10)
{
//Found valid combination - add to result list
resultList.Add(result);
//Move index pointers
highIndex++;
lowIndex--;
//Reset temporary result list
result = new List<int>();
result.Add(numbersOrdered[highIndex]);
sum = numbersOrdered[highIndex];
}
else
{
//Didn't find valid combination. Try to add the next lowest number
lowIndex--;
}
}
然而,它确实很快就变成了错误的做法。因此,我目前正在尝试这种看似更稳固的方法,但仍然存在一个小问题。
private void FindBestMatches(List<int> numbersOrdered, List<List<int>> resultList)
{
while (true)
{
var bestMatch = new List<int>();
var combinations = GetPowerSet(numbersOrdered);
foreach (var combination in combinations)
{
int sum = combination.Sum();
if (sum == 10) //Found perfect match - use this combination
{
bestMatch = combination.ToList();
break;
}
else if (sum > 10 && (!bestMatch.Any() || sum < bestMatch.Sum()))
{
bestMatch = combination.ToList();
}
}
//If we found a valid solution, remove these values from the ordered list
if (bestMatch.Any())
{
AddCombinationToResults(bestMatch, numbersOrdered, resultList);
continue;
}
break;
}
}
private static void AddCombinationToResults(IEnumerable<int> combination, List<int> numbersOrdered, List<List<int>> resultList)
{
//Remove the numbers in the result from the ordered numbers list
combination.ForEach(item => numbersOrdered.Remove(item));
//Add the result to the list of pools
resultList.Add(combination.ToList());
}
public IEnumerable<IEnumerable<T>> GetPowerSet<T>(List<T> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i];
}
使用这种方法时,似乎我得到了正确数量的解决方案,但它们没有得到优化 - 例如与{7,6,5,4}我得到{6,4}和{7,5}而不是{7,4}和{6,5},这很明显,因为我寻找最小的总和。但我无法弄清楚找到11和11而不是10和12的方法?
答案 0 :(得分:0)
经过多次试验和错误后,我想出了这个解决方案。它并不完美或快速,但它会在我的情况下完成。
private List<List<decimal>> FindBestMatches(List<decimal> numbersOrdered)
{
var resultList = new List<List<decimal>>();
if (numbersOrdered.Sum() < 10)
{
return resultList;
}
var possibleSumsWithinRange = GetPowerSet(numbersOrdered).Select(combination => combination.Sum()).Distinct().Where(sum => sum>= 10 && sum < 20).OrderBy(sum => sum);
//Remove highest sum if there are more than one possible sum
if (possibleSumsWithinRange.Count() > 1)
{
possibleSumsWithinRange.ToList().RemoveAt(possibleSumsWithinRange.Count() - 1);
}
//Go through all possible sums in order to find best result (lowest highest sum)
foreach (var possibleSum in possibleSumsWithinRange)
{
//Console.WriteLine("Sum:" + possibleSum);
var subResultList = new List<List<decimal>>();
var numbers = numbersOrdered.ToList();
while (true)
{
var combinations = GetPowerSet(numbers);
var bestMatch = new List<decimal>();
foreach (var combination in combinations)
{
decimal sum = combination.Sum();
if (sum < possibleSum)
{
continue;
}
if (sum == possibleSum) //Found perfect match - use this combination
{
bestMatch = combination.ToList();
break;
}
else if (sum > possibleSum && (!bestMatch.Any() || sum < bestMatch.Sum()))
{
bestMatch = combination.ToList();
}
}
//If we found a valid solution, remove these values from the ordered list and use same approach on the remaining values
if (bestMatch.Any())
{
bestMatch.ForEach(item => numbers.Remove(item));
subResultList.Add(bestMatch.ToList());
continue;
}
//If the new solution is the first or better than the previous, then promote it to result
if (!resultList.Any() || IsBetterSolution(resultList, subResultList))
{
resultList = subResultList.ToList();
}
break;
}
}
return resultList;
}
public bool IsBetterSolution(List<List<decimal>> previousSolution, List<List<decimal>> newSolution)
{
//If the new solution has more pools than the previous, then it is considered better
if (newSolution.Count > previousSolution.Count)
{
return true;
}
//If they have the same amount of pools, then choose the solution with the lowest highest sum
return (newSolution.Count == previousSolution.Count) && (highestSum(newSolution) < highestSum(previousSolution));
}
public decimal highestSum(List<List<decimal>> solution)
{
return solution.Max(x => x.Sum());
}
public IEnumerable<IEnumerable<T>> GetPowerSet<T>(List<T> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i];
}