c# – 具有相同权重/值的修改背包/子集和
作者:互联网
我正在研究一个必须处理背包/子集和问题的特殊情况的问题.问题如下:
您有一组随机大小递减的包大小,如:{47,35,22,…}.您的值是小部件的数量,如:#widgets = 33.
找到可以构成捆绑包的小部件数量的最少数量的捆绑包.如果没有办法返回等于数量的集合,则返回null.
>例1:
>捆绑尺寸:46,25,12,4,3
>数量:30
>返回值:{46:0,25:0,12:2,4:0,3:2}(包大小:包的数量)
>例2:
>捆绑尺寸:46,25,12,4,3
>数量:17
>返回值:{46:0,25:0,12:0,4:2,3:3}
>例3:
>捆绑尺寸:46,25,12,4,3
>数量:47
>返回值:{46:0,25:1,12:1,4:1,3:2}
>例4:
>捆绑尺寸:46,25,12,4,3
>数量:5
>返回值:null
怎么会出现这个问题?
我已经在C#中编写了一个程序,它可以完成足够多的工作,但是会在for循环中重置一个索引来转储bundle大小,这些大小不能与set的其余部分一起使用.如果要求它会在多长时间内发布消息来源.
请求的代码:
public List<int> BreakDown(List<int> bunSize, int buySize)
{
List<int> tempBunSize = bunSize.ToList();
tempBunSize.RemoveAll(e => e > buySize);
List<int> ammountNeeded = new List<int>();
int result = buySize;
for (int index = 0; index < tempBunSize.Count; index++)
{
int size = tempBunSize[index];
int tempResult = 0;
double calcResult = 0;
int addAmmount = 0;
if (index == tempBunSize.Count - 2)
{
tempResult = result % size;
if ((tempBunSize.Count > 2 || result % tempBunSize[tempBunSize.Count - 1] == 0))
{
if (result % size != 0)
{
ammountNeeded.Add(0);
tempResult = result % tempBunSize[tempBunSize.Count - 1];
if (tempResult != 0)
{
tempResult = result;
int sizeInc = 0;
while (tempResult != 0)
{
tempResult = tempResult - size;
sizeInc++;
if (tempResult < 0)
{
int decreaseOne = ammountNeeded.First();
ammountNeeded[0] = --decreaseOne;
result = result + tempBunSize.First();
if (ammountNeeded[0] <= 0)
{
tempBunSize.RemoveAt(0);
index = 0;
ammountNeeded = new List<int>();
}
ammountNeeded.Remove(0);
--index;
break;
}
if (tempResult % tempBunSize[tempBunSize.Count - 1] == 0)
{
ammountNeeded.Remove(0);
ammountNeeded.Add(sizeInc);
ammountNeeded.Add(tempResult / tempBunSize[tempBunSize.Count - 1]);
break;
}
}
if (tempResult < 0) continue;
break;
}
else
{
calcResult = result / tempBunSize[tempBunSize.Count - 1];
addAmmount = (int)Math.Floor(calcResult);
ammountNeeded.Add(addAmmount);
break;
}
}
else if (result % size == 0)
{
tempResult = result % size;
if (tempResult != 0 && result % tempBunSize[tempBunSize.Count - 1] != 0)
{
int decreaseOne = ammountNeeded.First();
ammountNeeded.Insert(0, --decreaseOne);
result = result + tempBunSize.First();
continue;
}
else
{
calcResult = result / size;
addAmmount = (int)Math.Floor(calcResult);
ammountNeeded.Add(addAmmount);
if (result % size == 0)
{
ammountNeeded.Add(0);
break;
}
calcResult = result / tempBunSize[tempBunSize.Count - 1];
addAmmount = (int)Math.Floor(calcResult);
ammountNeeded.Add(addAmmount);
break;
}
}
}
else if (tempResult % tempBunSize[tempBunSize.Count - 1] != 0)
{
tempResult = result;
int sizeInc = 0;
while (tempResult != 0)
{
tempResult = tempResult - size;
sizeInc++;
if (tempResult % tempBunSize[tempBunSize.Count - 1] == 0)
{
ammountNeeded.Add(sizeInc);
ammountNeeded.Add(tempResult / tempBunSize[tempBunSize.Count - 1]);
break;
}
}
break;
}
else if (result == 0)
{
while (ammountNeeded.Count < bunSize.Count)
{
ammountNeeded.Add(0);
}
break;
}
else
{
calcResult = result / size;
ammountNeeded.Add((int)Math.Floor(calcResult));
calcResult = tempResult / tempBunSize[tempBunSize.Count - 1];
ammountNeeded.Add((int)Math.Floor(calcResult));
break;
}
}
ammountNeeded.Add((int)Math.Floor((decimal)result / size));
result = result % size;
if (result == 0) continue;
}
if (ammountNeeded.Count < bunSize.Count)
{
ammountNeeded.Reverse(0, ammountNeeded.Count);
while (ammountNeeded.Count < bunSize.Count)
{
ammountNeeded.Add(0);
}
ammountNeeded.Reverse(0, ammountNeeded.Count);
}
if (ammountNeeded.FindLast(e => e < 0) < 0 || ammountNeeded.Sum() == 0)
return null;
return ammountNeeded;
}
}
解决方法:
这是一个有待解决的有趣问题.极客指向四周.
我认为你的主要问题是尝试循环遍历单个列表.你真正想要的是测试列表的所有变体然后找到具有最高值的变体.
此外,正如评论中所述,递归是你的朋友.我通过捆绑量的每个排列递归.
您的数据存在的一个问题是您的示例.这个例子中使用的数学是贪婪的.意思是,4将尝试获得尽可能多的数据,然后将剩余部分移交给3. 4因此获得4个包,并且返回1个余数为null.出于这个原因,我认为17的正确答案应为空.你或许可以弄清楚如何在数字之间取得平衡,但这将是IMO的更多逻辑.
这是代码:
public class test
{
public static void Main()
{
var bundleSizes = new List<int> { 46, 25, 12, 4, 3 };
var quantity = 30;
var bundleResults = Begin(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 17;
bundleResults = Begin(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 47;
bundleResults = Begin(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 5;
bundleResults = Begin(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
Console.Read();
}
static void Output(List<int> bundleSizes, int quantity, Dictionary<int, int> bundleResults)
{
var fullResults = new Dictionary<int, int>();
bundleSizes.ForEach(
delegate(int e)
{
var result = 0;
if(bundleResults != null)
bundleResults.TryGetValue(e, out result);
fullResults.Add(e, result);
});
Console.WriteLine("Bundle Sizes: {0}", string.Join(",", bundleSizes));
Console.WriteLine("Quantity: {0}", quantity);
Console.WriteLine("Returned Value: {0}", bundleResults == null ? "null" : fullResults.Aggregate("", (keyString, pair) => keyString + pair.Key + ":" + pair.Value + ","));
}
static Dictionary<int, int> Begin(List<int> bundleSizes, int quantity)
{
var bundleCombinations = GetCombination(bundleSizes);
var tempBundleSizes = new List<Dictionary<int, int>>();
foreach (var bundleCombination in bundleCombinations)
{
var tempBundleSize = new Dictionary<int, int>();
bundleCombination.ForEach(e => tempBundleSize.Add(e, 0));
var results = Bundle(bundleCombination, quantity);
if (results == null)
continue;
foreach (var result in results)
{
tempBundleSize[result.Key] = result.Value;
}
tempBundleSizes.Add(tempBundleSize);
}
var longest = tempBundleSizes.OrderByDescending(x => x.Count).FirstOrDefault();
return longest;
}
static List<List<int>> GetCombination(List<int> list)
{
var retValue = new List<List<int>>();
var count = Math.Pow(2, list.Count);
for (var i = 1; i <= count - 1; i++)
{
var retList = new List<int>();
var str = Convert.ToString(i, 2).PadLeft(list.Count, '0');
for (var j = 0; j < str.Length; j++)
{
if (str[j] == '1')
{
retList.Add(list[j]);
}
}
retValue.Add(retList);
}
return retValue;
}
static Dictionary<int, int> Bundle(List<int> bundleSizes, int quantity)
{
var bundleQuantities = new Dictionary<int, int>();
bundleSizes.ForEach(delegate(int k)
{
if (k <= quantity)
bundleQuantities.Add(k, 0);
});
return bundleQuantities.Count == 0 ? null : RecurseBundles(quantity, bundleQuantities.Keys.ToList(), bundleQuantities);
}
static Dictionary<int, int> RecurseBundles(int quantity, List<int> bundleSizes, Dictionary<int, int> bundleQuantities)
{
var bundleSize = bundleSizes.First();
var bundles = (int)Math.Floor((double)quantity / bundleSize);
var remainingQuantity = quantity % bundleSize;
bundleQuantities[bundleSize] = bundles;
var remainingBundles = bundleSizes.Skip(1).ToList();
if (!remainingBundles.Any() && remainingQuantity > 0)
bundleQuantities = null;
if (remainingBundles.Any())
bundleQuantities = RecurseBundles(remainingQuantity, remainingBundles, bundleQuantities);
return bundleQuantities;
}
}
这是输出:
Bundle Sizes: 46,25,12,4,3
Quantity: 30
Returned Value: 46:0,25:0,12:2,4:0,3:2,
Bundle Sizes: 46,25,12,4,3
Quantity: 17
Returned Value: null
Bundle Sizes: 46,25,12,4,3
Quantity: 47
Returned Value: 46:0,25:0,12:3,4:2,3:1,
Bundle Sizes: 46,25,12,4,3
Quantity: 5
Returned Value: null
特别感谢这些出色的SO答案:
All Possible Combinations of a list of Values
How do I combine the keys and values of a Dictionary into one List using LINQ?
Find max count of a list of custom types
编辑:在输出方法中对更好的格式化输出进行了一些小改动
标签:c,algorithm,knapsack-problem,subset-sum 来源: https://codeday.me/bug/20190629/1324350.html