寻找解决这个动态编程问题的提示

Looking for hints to solve this dynamic programming problem

本文关键字:问题 提示 编程 动态 解决 寻找      更新时间:2023-10-16

我正在努力提高我在编程面试中解决问题的能力,并试图解决这个问题。我有一种感觉,它可以使用动态编程来解决,但递归关系对我来说并不明显。

为了选择前三位合唱团歌手,我只需使用蛮力。因为只有20 Choose 3 = 1140种方法来挑选它们。起初我以为dp[a][b][c]可以代表最短的歌曲,有三个合唱团歌手,还有剩余的呼吸a, b, c。如果我可以用dp[a][b][c] = 1 + dp[a - 1][b - 1][c - 1]来计算,但是当任何指数等于0时应该怎么做,哪个合唱团歌手应该被替换。此外,我们不能重用dp数组,因为假设在一个实例中,我们从具有呼吸a, b, c的合唱团歌手开始,在第二个实例中d, e, f.一旦计算了第一个实例并填充了dp数组;第二个实例可能需要使用第一个实例计算的dp[i][j][k]。由于此值首先取决于可用的合唱团歌手,并且两个实例中的可用歌手不同,因此在第二个实例中可能无法dp[i][j][k]。这是因为最短的歌曲长度dp[i][j][k]可能会使用合唱团歌手,在第二种情况下已经使用。

我没有解决这个问题的想法,任何地方都没有解决方案。有人可以给我一些提示来解决它吗?

问题陈述

我们有N歌手,他们每个人都有一定的时间可以唱歌,一旦气喘吁吁,需要 1 秒钟才能恢复。他们可以唱的最小歌曲是多少,三个歌手一直在唱,他们三个同时唱完

?输入:

输入 3<= 20 N 个整数 Fi(1 <= Fi <= 10,对于所有 1 个<= i <= N(

这是这个想法。

在歌唱的每个点上,当前状态都可以通过歌手是谁、他们唱了多长时间以及哪些人目前气喘吁吁来表示。 从每个状态我们需要过渡到一个新的状态,即每个歌手气喘吁吁地准备再次唱歌,每个歌手唱歌都好少转一圈,可能会选择新的歌手。

天真地完成,最多有 20 位选择 3 位歌手,每个歌手都可以处于 10 种当前状态,再加上多达 2 位气喘吁吁的人。 这是您可以处于的 175560000 个组合状态。 这太多了,我们需要更聪明才能完成这项工作。

更聪明的是,我们没有20个可区分的歌手。 我们有 10 桶歌手,根据他们能唱多久。 如果一个歌手可以唱7圈,那么如果目前正在唱歌,他们不可能处于10个状态,而只能是7个。 我们不在乎两个人能唱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功能的良好表示。

记忆方面的状态似乎与开始以来经过的时间无关。采取任何起始位置,

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

蟒蛇代码:

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)