如何计算具有多个限制的排列?

时间:2019-06-27 04:56:02

标签: c# recursion permutation

想象一下,我们有一些袋子的种子,每个袋子包含不同种类的树。这些袋子按从高到低的顺序排列。

E.g. bag = { 1, 2, 3, 4, 5 }, bag[i] = seed quantity; species of bag[0] taller then species of bag[1];

我被要求计算所有可能满足3种条件的所有种子的种植方式:

  • 左种子总是比右种子高。
  • 在同一行位置,种子始终低于上一行的种子。
  • 一行总是比下一行长。
E.g. With the bag = { 2, 2 }, these are 8 ways to plant.
Define species of bag[0] is 2, bag[1] is 1:

 2 2 1 1

 2 2 1
 1

 2 1
 2 1

 2 2
 1
 1

 2
 2
 1
 1

 2 1
 2
 1

 2 1 1
 2

 2 2
 1 1

我认为这是排列计数问题,对吗?

我尝试了一种递归方法来解决该问题:

int PlantTree(int[] bag)
{
    if (bag.Length < 1) return 0;

    // Initialize top row
    var top = new int[bag.Sum()];

    // Get the first seed
    var fs = 0;
    for (; fs < bag.Length; fs++)
        if (bag[fs] > 0) break;

    bag[fs]--;

    var ret = 0;
    for (var i = 1; i <= bag.Sum() + 1; i++)
    {
        PlantRow(bag, top, 0, 1, i, ref ret);
    }
    return ret;
}

void PlantRow(int[] bag, int[] top, int sIdx, int rIdx, int rLen, ref int count)
{
    var sum = bag.Sum();
    if (sum == 0)
    {
        count++;
        return;
    }

    if (rIdx == rLen)
    {
        var fs = 0;
        for (; fs < bag.Length; fs++)
            if (bag[fs] > 0) break;

        bag[fs]--;

        // Plant next row
        var len = sum < rLen ? sum : rLen;
        for (var i = 1; i <= len; i++)
        {
            PlantRow(bag, top, 0, 1, i, ref count);
        }

        bag[fs]++;
        return;
    }

    for (; sIdx < bag.Length; sIdx++)
    {
        if (bag[sIdx] <= 0) continue;
        if (top[rIdx] > sIdx)
        {
            sIdx = top[rIdx] - 1;
            continue;
        }

        // Place seed into top
        var sTop = top[rIdx];
        top[rIdx] = sIdx;
        bag[sIdx]--;

        PlantRow(bag, top, sIdx, rIdx + 1, rLen, ref count);

        bag[sIdx]++;
        top[rIdx] = sTop;
    }
}

我的代码仅需少量种子即可正常工作,例如bag = {4,4,4,4,4}。

但是对于更大的集合,例如bag = {8,8,8,8,8},它的运行时间非常长(几小时,甚至几天)。

我的递归方法有什么错误吗? 还是有另一种方法可以解决此排列计数问题?

非常感谢

0 个答案:

没有答案