我想要在可变的小时数内传播可变数量的项目。我遇到的问题是如何分配余数,以便在"悬垂"之间留出空间。尽可能相等。例如,如果我有5个小时的13个项目(X),我希望最终得到
Hours: 1 2 3 4 5
---------------------------------
x x x x x
x x x x x
x x x
我不确定我是否过度思考这个问题。我目前正在检查物品数量是否大于小时数。如果这是真的,我划分(项目数/小时数)。然后我认为我必须划分(小时数/余数)...但是对于上面的例子:5/3 = 1.6,它转向2.我认为我必须以某种方式使用Math.Floor
,但是我目前还不确定如何。
对于5个小时内的4个项目,我想最终得到X. 对于Ys的2个项目 对于带有Zs
的1个项目 1 2 3 4 5
------------------------
x x x x
y y
z
项目数和小时数是可变的。
好的,我想我现在正走在正确的轨道上。我现在试图将垃圾箱分成两半,然后把其中一个放在中间垃圾箱里。这会递归重复,直到余数为0。
答案 0 :(得分:4)
编辑:修复了连播时间和物品的问题。
这是一个难题,下面是解决方案。我已经为完全通用的问题编写了解决方案,因此它适用于任意时间和项目数。以下是示例输出:
Items=10, Hours=14
XX XX XX XX XX
Items=11, Hours=14
XX XXXXX XX XX
Items=12, Hours=14
XX XXXXXXXX XX
Items=16, Hours=13
XXXXXXXXXXXXX
XXX
Items=17, Hours=13
XXXXXXXXXXXXX
X X X X
Items=18, Hours=13
XXXXXXXXXXXXX
X XXX X
Items=19, Hours=13
XXXXXXXXXXXXX
X X X X X X
Items=20, Hours=13
XXXXXXXXXXXXX
X X XXX X X
Items=21, Hours=13
XXXXXXXXXXXXX
X XX X X XX X
以下是解决方案的工作原理:
这是代码:
static void Main(string[] args)
{
var hours = 13;
for (var items = 16; items < 22; items++)
PrintDistribution(items, hours);
}
private static void PrintDistribution(int items, int hours)
{
Console.WriteLine(string.Format("\nItems={0}, Hours={1}", items, hours));
for (var i = 0; i < (items / hours) * hours; i++)
{
Console.Write('X');
if ((i + 1) % hours == 0)
Console.WriteLine();
}
var line = new StringBuilder(new string(' ', hours));
var remaining = items % hours;
var evens = remaining / 2;
var odd = remaining - (evens * 2);
var seq = BinaryTreeSequence(hours / 2).GetEnumerator();
for (var i = 0; i < evens; i++)
{
seq.MoveNext();
line[seq.Current] = 'X';
line[hours - seq.Current - 1] = 'X';
}
if (odd > 0)
if (hours % 2 == 0)
{
seq.MoveNext();
line[seq.Current] = 'X';
}
else
line[hours / 2] = 'X';
Console.WriteLine(line);
}
public static IEnumerable<int> BinaryTreeSequence(int count)
{
if (count > 1)
yield return count - 1;
if (count > 0)
yield return 0;
var seqQueue = new Queue<Tuple<int, int, int>>();
Enqueue(seqQueue, 0, count - 1);
for (var seqIndex = count - 2; seqIndex > 0; seqIndex--)
{
var moreNeeded = seqQueue.Count < seqIndex;
var seq = seqQueue.Dequeue();
yield return seq.Item1;
if (moreNeeded)
{
Enqueue(seqQueue, seq.Item1, seq.Item3);
Enqueue(seqQueue, seq.Item2, seq.Item1);
}
}
}
private static void Enqueue(Queue<Tuple<int, int, int>> q, int min, int max)
{
var midPoint = (min + max) / 2;
if (midPoint != min && midPoint != max)
q.Enqueue(Tuple.Create(midPoint, min, max));
}
答案 1 :(得分:1)
这是一个近似的解决方案。它返回带有从零开始的索引和项目的元组。 (我认为这些项目可能很重要,而不仅仅是虚拟值,例如你的x
s)在某些情况下,它没有选择最佳间距,但我认为它总是很接近(即间隙不超过1大于必要的),并始终返回正确数量的项目。
public static IEnumerable<Tuple<int, T>> SplitItems<T>(IEnumerable<T> items, int count)
{
var itemList = items.ToList();
int lastRowCount = itemList.Count % count;
int wholeRowItemCount = itemList.Count - lastRowCount;
// return full rows: 0 <= i < wholeRowCount * count
for (int i = 0; i < wholeRowItemCount; i++)
{
yield return Tuple.Create(i % count, itemList[i]);
}
if (lastRowCount > 0)
{
//return final row: wholeRowCount * count <= i < itemList.Count
double offset = (double)count / (lastRowCount + 1);
for (double j = 0; j < lastRowCount; j++)
{
int thisIntPos = (int)Math.Round(j * count / (lastRowCount + 1) + offset, MidpointRounding.AwayFromZero);
yield return Tuple.Create(thisIntPos, itemList[wholeRowItemCount + (int)j]);
}
}
}
作为如何使用它的一个例子:
Console.WriteLine(string.Join("\r\n", SplitItems(Enumerable.Range(1, 12), 5)));
// prints
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(0, 6)
(1, 7)
(2, 8)
(3, 9)
(4, 10)
(2, 11)
(3, 12)
(这不是最理想的,因为最后一行的项目为2-3,空格/间隙为0-1和4,而y
的解决方案只有大小为1的差距。
此外,尽管它与您的示例(在我的基于零的索引中为0,2,4)不匹配,但以下示例满足您到目前为止定义的算法,因为它最小化了间隙大小。 (索引0和2处的1个大小的间隙,而不是你的,在1和3处有间隙)如果0, 2, 4
确实比1, 3, 4
更好,则需要确定为什么确切地说,并将其添加到算法定义中。
Console.WriteLine(string.Join("\r\n", SplitItems(Enumerable.Range(1, 3), 5)));
// prints
(1, 1)
(3, 2)
(4, 3)
实际上,这是一种restricted partition问题。要在d
小时内对h
项进行分割,您希望找到h-d
的分区,其中h-d
个分区不超过max(parts)
个max(parts) == 1
最小的分区。例如。在5小时内划分2个项目:最佳解决方案是1 + 1 + 1,因为它不超过3个部分,0,2,4
,这是你能做的最好的。作为一个没有单一解决方案的示例,5个小时中的3个项目有1 + 1,但有不同的方式来安排它,包括1,3,4
,0,2,3
和{{1}}。