4

この困難な問題の最適な解決策を決定しようとしています。長さがあります (11 としましょう)。つまり、0 ~ 10 の 1 次元空間です。これで、これらの間隔が同じ長さになりました (この例では 2 と仮定します)。現在、それらはランダムに分散されています (重複しているかどうかに関係なく)。例を挙げてみましょう:

Situation:

|00|01|02|03|04|05|06|07|08|09|10| <- Space (length = 11)

|-----|       
         |-----|
            |-----|
               |-----|
                  |-----|
                           |-----| <- single interval of length = 2

ここで、解決策は、重なり合うことなく一度に収まる間隔の最大数を見つける必要があります。

The solution is: 4 intervals

4 つの間隔の 3 つの結果があります。

|00|01|02|03|04|05|06|07|08|09|10|

|-----|  |-----|-----|     |-----| <- result 1
|-----|  |-----|  |-----|  |-----| <- result 2
|-----|     |-----|-----|  |-----| <- result 3

しかし、さらに 2 つの制約もあります。

  1. より多くの結果がある場合 (最良の解、この場合 = 4)、ギャップの数が最も少ない結果。

  2. それ以上の結果がある場合でも、すべてのスペースの最小長が最も高い結果が返されます。たとえば、(長さの) 2 & 3 のスペースを持つものは、スペースの最小の長さ = 2 です。これは、スペースの最小の長さが 1 しかない 1 & 4 よりも優れています。

したがって、結果 2 には 4 つの「継続的な」チャンクがあり、他の 2 つには 3 つしかないため、改良は次のようになります。

|00|01|02|03|04|05|06|07|08|09|10|

|-----|  |-----------|     |-----| <- result 1
|-----|     |-----------|  |-----| <- result 3

これらの 2 つのスペースの分布は同じなので、最初の 1 つを取り上げます。

入力セットの結果は次のとおりです。

Interval count  : 4
Optimal solution: |-----|  |-----------|     |-----|

アルゴリズムは、すべてのスペース長 (11 だけでなく)、すべての間隔長 (間隔の長さは常に <= スペース長)、および任意の数の間隔に対して普遍的に機能する必要があります。

アップデート:

問題のあるシナリオ:

|00|01|02|03|04|05|06|07|08|09|

|-----|  
         |-----| 
            |-----|     
                        |-----|
|-----|
4

1 に答える 1

4

これは単純な動的計画問題です。

全長Nをとし、タスクの長さをL

サブF(T)間隔から選択できるタスクの最大数とすると(T, N)、各単位時間Tで、次の3つの可能性があります。

  1. Tから始まるタスクはありません。
  2. Tから始まるタスクがありますが、結果セットには含まれていません。
  3. Tから始まるタスクがあり、それを結果セットに含めます。

ケース1は単純で、次のようになりますF(T) = F(T + 1)

ケース2/3の場合、開始するタスクを選択すると、このタスクの実行中に開始するすべてのタスクを拒否する必要があることに注意してください。Tつまり、Tとの間T + Lです。だから私たちは得るF(T) = max(F(T + 1), F(T + L) + 1)

最後に、F(N) = 0。つまり、最初から始めてF(N)、に戻っていくだけF(0)です。

編集:これにより、間隔の最大数が得られますが、2つの制約を満たすセットは得られません。制約についてのあなたの説明は私には不明確なので、そこであなたを助ける方法がわかりません。特に、サンプルセットのすべての解が明らかに等しいため、制約1が何を意味するのかわかりません。

編集2:要求に応じていくつかのさらなる説明:

あなたの投稿された例を考えてみましょN = 11L = 2。で始まるタスクがありますT = 0, 3, 4, 5, 6, 9。開始しF(11) = 0て逆方向に作業する:

  • F(11) = 0
  • F(10) = F(11) = 0(でタスクが開始されないためT = 10
  • F(9) = max(F(10), F(11) + 1) = 1
  • ..。

最終的に私たちはに到達しF(0) = 4ます:

T   |00|01|02|03|04|05|06|07|08|09|10|
F(T)| 4| 3| 3| 3| 3| 2| 2| 1| 1| 1| 0|

編集3:まあ、私はこれについて十分に興味があったので、解決策を書いたので、それを投稿したほうがいいかもしれません。これにより、タスクが最も多く、ギャップの数が最も少なく、最小のギャップが最小のセットが得られます。質問の例の出力は次のとおりです。

  • (0, 2) -> (4, 6) -> (6, 8) -> (9, 11)
  • (0, 2) -> (4, 6) -> (8, 10)

明らかに、私は正確さについて保証しません!:)

プライベートクラスTask{publicint Start {get; セットする; } public int Length {get; セットする; } public int End {get {return Start + Length; }}

    public override string ToString()
    {
        return string.Format("({0:d}, {1:d})", Start, End);
    }
}

private class CacheEntry : IComparable
{
    public int Tasks { get; set; }
    public int Gaps { get; set; }
    public int MinGap { get; set; }
    public Task Task { get; set; }
    public Task NextTask { get; set; }

    public int CompareTo(object obj)
    {
        var other = obj as CacheEntry;
        if (Tasks != other.Tasks)
            return Tasks - other.Tasks; // More tasks is better
        if (Gaps != other.Gaps)
            return other.Gaps = Gaps; // Less gaps is better
        return MinGap - other.MinGap; // Larger minimum gap is better
    }
}

private static IList<Task> F(IList<Task> tasks)
{
    var end = tasks.Max(x => x.End);
    var tasksByTime = tasks.ToLookup(x => x.Start);
    var cache = new List<CacheEntry>[end + 1];

    cache[end] = new List<CacheEntry> { new CacheEntry { Tasks = 0, Gaps = 0, MinGap = end + 1 } };

    for (int t = end - 1; t >= 0; t--)
    {
        if (!tasksByTime.Contains(t))
        {
            cache[t] = cache[t + 1];
            continue;
        }

        foreach (var task in tasksByTime[t])
        {
            var oldCEs = cache[t + task.Length];
            var firstOldCE = oldCEs.First();
            var lastOldCE = oldCEs.Last();

            var newCE = new CacheEntry
            {
                Tasks = firstOldCE.Tasks + 1,
                Task = task,
                Gaps = firstOldCE.Gaps,
                MinGap = firstOldCE.MinGap
            };

            // If there is a task that starts at time T + L, then that will always 
            // be the best option for us, as it will have one less Gap than the others
            if (firstOldCE.Task == null || firstOldCE.Task.Start == task.End)
            {
                newCE.NextTask = firstOldCE.Task;
            }
            // Otherwise we want the one that maximises MinGap.
            else
            {
                var ce = oldCEs.OrderBy(x => Math.Min(x.Task.Start - newCE.Task.End, x.MinGap)).Last();
                newCE.NextTask = ce.Task;
                newCE.Gaps++;
                newCE.MinGap = Math.Min(ce.MinGap, ce.Task.Start - task.End);
            }

            var toComp = cache[t] ?? cache[t + 1];
            if (newCE.CompareTo(toComp.First()) < 0)
            {
                cache[t] = toComp;
            }
            else
            {
                var ceList = new List<CacheEntry> { newCE };

                // We need to keep track of all subsolutions X that start on the interval [T, T+L] that
                // have an equal number of tasks and gaps, but a possibly a smaller MinGap. This is
                // because an earlier task may have an even smaller gap to this task.
                int idx = newCE.Task.Start + 1;
                while (idx < newCE.Task.End)
                {
                    toComp = cache[idx];
                    if
                    (
                        newCE.Tasks == toComp.First().Tasks &&
                        newCE.Gaps == toComp.First().Gaps &&
                        newCE.MinGap >= toComp.First().MinGap
                    )
                    {
                        ceList.AddRange(toComp);
                        idx += toComp.First().Task.End;
                    }
                    else
                        idx++;
                }

                cache[t] = ceList;
            }
        }
    }

    var rv = new List<Task>();
    var curr = cache[0].First();
    while (true)
    {
        rv.Add(curr.Task);
        if (curr.NextTask == null) break;
        curr = cache[curr.NextTask.Start].First();
    }

    return rv;
}

public static void Main()
{
    IList<Task> tasks, sol;

    tasks = new List<Task>
    {
        new Task { Start = 0, Length = 2 },
        new Task { Start = 3, Length = 2 },
        new Task { Start = 4, Length = 2 },
        new Task { Start = 5, Length = 2 },
        new Task { Start = 6, Length = 2 },
        new Task { Start = 9, Length = 2 },
    };

    sol = F(tasks);
    foreach (var task in sol)
        Console.Out.WriteLine(task);
    Console.Out.WriteLine();

    tasks = new List<Task>
    {
        new Task { Start = 0, Length = 2 },
        new Task { Start = 3, Length = 2 },
        new Task { Start = 4, Length = 2 },
        new Task { Start = 8, Length = 2 },
    };

    sol = F(tasks);
    foreach (var task in sol)
        Console.Out.WriteLine(task);
    Console.Out.WriteLine();

    tasks = new List<Task>
    {
        new Task { Start = 0, Length = 5 },
        new Task { Start = 6, Length = 5 },
        new Task { Start = 7, Length = 3 },
        new Task { Start = 8, Length = 9 },
        new Task { Start = 19, Length = 1 },
    };

    sol = F(tasks);
    foreach (var task in sol)
        Console.Out.WriteLine(task);
    Console.Out.WriteLine();

    Console.In.ReadLine();
}
于 2012-08-26T15:07:12.507 に答える