web-dev-qa-db-ja.com

この動的プログラミング問題を解決するためのヒントを探しています

私はインタビューをプログラミングするための問題解決スキルを向上させようとしています この問題 を解決しようとしています。動的プログラミングを使用して解決できると感じていますが、再帰的な関係は明らかではありません。

最初の3つの合唱歌手を選択するには、ブルートフォースを使用します。それらを選ぶ方法は20 Choose 3 = 1140しかないので。最初、私はdp[a][b][c]が3人の合唱団の歌手で息が残りにくいa, b, cを表すことができると思いました。 dp[a][b][c] = 1 + dp[a - 1][b - 1][c - 1]を使用してこれを計算できたが、インデックスのいずれかが0に等しい場合に実行する必要がある場合、合唱歌手を置き換える必要があります。さらに、dp配列を再利用することはできません。なぜなら、1つのインスタンスではブレスa, b, cの合唱団の歌手から始め、2つ目のインスタンスではd, e, fから始めるからです。最初のインスタンスが計算され、dp配列が満たされると、 2番目のインスタンスは、最初のインスタンスによって計算されたdp[i][j][k]を使用する必要がある場合があります。この値は、最初のインスタンスで使用可能な合唱歌手に依存し、両方のインスタンスで使用可能な歌手は同じではないため、2番目のインスタンスではdp[i][j][k]を使用できない場合があります。これは、最短の曲の長さdp[i][j][k]が、2番目のインスタンスですでに使用されている合唱歌手を使用する可能性があるためです。

私はこの問題に取り組むためのアイデアがなく、どこにも解決策はありません。誰かがそれを解決するためのヒントを教えてくれませんか?

問題文

私たちにはN人の歌手がいますが、歌手には一定の時間があり、息を切らしてから回復するには1秒必要です。 3人の歌手が常に歌っていて、3人全員が同時に歌っている、彼らが歌うことができる最小の歌は何ですか?

入力:

入力3 <N <= 20 N整数Fi(1 <= Fi <= 10、すべて1 <= i <= N)

2
user13533384

これがアイデアです。

歌の各時点で、現在の状態は、歌手が誰であるか、歌手がどれだけ長い間歌っているか、現在どれが息切れしているかによって表すことができます。そして、各状態から新しい状態に移行する必要があります。つまり、息を切らしたすべての歌手が再び歌う準備ができており、すべての歌手が1ターン少ないので、新しい歌手が選ばれる可能性があります。

簡単に言えば、最大20人が3人の歌手を選び、それぞれが現在の10の状態になり、さらに最大2人が息切れします。これは175560000の組み合わせ可能な状態です。多すぎます。この機能を動作させるには、より賢くする必要があります。

より賢く、notには20の区別可能な歌手がいます。彼らが歌うことができる時間に基づいて、私たちは歌手の10バケットを持っています。歌手が7ターン歌える場合、彼らはできない現在歌っている場合は10の状態になりますが、7しかありませんnotは、2人が7歌えるかどうかを気にしますターンは4と3ターン、または3と4ターンです。これは多くの対称性をもたらします。すべての対称性を処理すると、起こり得る状態の数が数億から(通常は)数万に減少します。

これで、DPの状態遷移がdp[state1]からdp[state2]になりました。データ構造のキーとして使用できるこれらの対称性を利用する状態表現を作成することが課題です。

更新:

コードのメインループは次のPythonのようになります。

while not finished:
    song_length += 1
    next_states = set()
    for state in current_states:
        for next_state in transitions(state):
            if is_finished(next_state):
                finished = True # Could break out of loops here
            else:
                next_states.add(next_state)
    current_states = next_states

課題のほとんどは、状態とtransitions関数の適切な表現です。

2
btilly

メモ化の状態は、開始からの経過時間とは無関係のようです。任意の開始位置を取る

a, b, c

どこ a, b, cは選択された大きさ(各歌手が息を止めることができる時間)であり、aは最小の大きさです。我々は持っています

a, b, c
t = 0

そしてそれは同じです:

0, b - a, c - a
t = a

したがって、最小の大きさaを使用して初期状態を次のように定義してみましょう。

b, c, ba, ca
  where ba = b - a
        ca = c - a
t = a

ここから、状態のすべての遷移は似ています。

new_a <- x
  where x is a magnitude in
  the list that can be available
  together with b and c. (We only
  need to try each such unique
  magnitude once during this
  iteration. We must also prevent
  a singer from repeating.)

  let m = min(new_a, ba, ca)

  then the new state is:
    u, v, um, vm
    t = t + m
      where u and v are from the
      elements of [new_a, b, c] that
      aren't associated with m, and um
      and vm are their pairs from
      [new_a, ba, ca] that aren't m,
      subtracted by m.

訪問した組み合わせのメモ化の状態は、次の場合のみです。

[(b, ba), (c, ca)] sorted by
the tuples' first element

到達した関連するtがその状態で見られる最小のものと等しいかそれ以上である場合、検索でブランチをプルーニングできます。

例:

2 4 7 6 5

解決策(トップダウンで読む):

4 5 6
7 4 5
  2

州:

u v um vm
5 6 1 2
t = 4

new_a = 7
m = min(7, 1, 2) = 1 (associated with 5)
7 6 6 1
t = 5

new_a = 4
m = min(4, 6, 1) = 1 (associated with 6)
4 7 3 5
t = 6

new_a = 5
m = min(5, 3, 5) = 3 (associated with 4)
5 7 2 2
t = 9

new_a = 2
m = min(2, 2, 2) = 2 (associated with 2)
5 7 0 0
t = 11

Pythonコード:

import heapq
from itertools import combinations

def f(A):
  mag_counts = {}

  for x in A:
    if x in mag_counts:
      mag_counts[x] = mag_counts[x] + 1
    else:
      mag_counts[x] = 1

  q = []

  seen = set()

  # Initialise the queue with unique starting combinations
  for comb in combinations(A, 3):
    sorted_comb = Tuple(sorted(comb))
    if not sorted_comb in seen:
      (a, b, c) = sorted_comb
      heapq.heappush(q, (a, (b-a, b), (c-a, c), a))
    seen.add(sorted_comb)

  while q:
    (t, (ba, b), (ca, c), prev) = heapq.heappop(q)

    if ba == 0 and ca == 0:
      return t

    for mag in mag_counts.keys():
      # Check that the magnitude is available
      # and the same singer is not repeating.
      [three, two] = [3, 2] if mag != prev else [4, 3]
      if mag == b == c and mag_counts[mag] < three:
        continue
      Elif mag == b and mag_counts[mag] < two:
        continue
      Elif mag == c and mag_counts[mag] < two:
        continue
      Elif mag == prev and mag_counts[mag] < 2:
        continue

      m = min(mag, ba, ca)

      if m == mag:
        heapq.heappush(q, (t + m, (ba-m, b), (ca-m, c), m))
      Elif m == ba:
        heapq.heappush(q, (t + m, (mag-m, mag), (ca-m, c), b))
      else:
        heapq.heappush(q, (t + m, (mag-m, mag), (ba-m, b), c))

  return float('inf')

As = [
  [3, 2, 3, 3], # 3
  [1, 2, 3, 2, 4], # 3
  [2, 4, 7, 6, 5] # 11
]

for A in As:
  print A, f(A)
1