如果使用动态编程来获得问题的最佳解决方案。您如何重建导致该解决方案的实际步骤?
例如,在0-1背包问题中,您使用了重复
使用这个我们可以获得背包中可以存在的最大值。你如何找到实际的物品。
这可以推广到任何动态编程解决方案。例如。找到实际的nos,它们是使用动态编程获得解决方案的最长增长子序列的一部分。
答案 0 :(得分:4)
这可以针对任何动态编程解决方案进行推广。
不,你不能一般通过检查DP表中的最终值来找到实际的解决方案。
如果算法只是寻找某个最佳值,它通常会丢弃有关 每个值的计算信息。
在DP解决方案中,行 R 上的单元格可能例如取决于行 R-1 中的最大值。除非算法记录了选择的单元格,否则将无法根据结果表重建实际解决方案。
但是,您应始终能够将附加信息附加到描述值来源的每个单元格,例如引用当前单元格依赖的先前计算的单元格,并使用此信息重建实际的解决方案。
答案 1 :(得分:2)
诀窍是存储其他信息,这些信息将允许您重新构建在每个步骤中所做的选择,同时填充动态编程表。有时,表本身包含此类信息。例如,在0/1背包问题中,您可以通过以下方式找到用于达到最佳解决方案的项目(请注意,只需要表格):
# 0/1 knapsack. O(nC) time, O(nC) space,
# also returns the index of the items to pick
# V: values, W: weights, C: capacity
def integral_knapsack_items(V, W, C):
table = integral_knapsack_table(V, W, C)
i, j, items = len(W), C, []
while i != 0 and j != 0:
if table[i][j] != table[i-1][j]:
items.append(i-1)
i, j = i-1, j-W[i-1]
else:
i -= 1
return (table[-1][-1], items)
def integral_knapsack_table(V, W, C):
m, n = len(W)+1, C+1
table = [[0] * n for x in xrange(m)]
for i in xrange(1, m):
for j in xrange(1, n):
if W[i-1] > j:
table[i][j] = table[i-1][j]
else:
table[i][j] = max(table[i-1][j],
V[i-1] + table[i-1][j-W[i-1]])
return table
在上面的代码中,您使用integral_knapsack_items()
(值数组),V
(相应权重数组)和W
(容量为{1}}来调用C
背包),程序返回一个元组,其中包含填充背包时获得的最大值,以及用于达到该值的项目的索引。
答案 2 :(得分:1)
您只需要重新访问DP中的步骤即可。在0-1背包的情况下,假设原始DP功能是求解,函数 reconstruct 将为您提供实际解决方案(我用C ++编写代码) ):
int solve(int pos, int capacity)
{
if(pos == no_of_objects) return 0;
if(memo[pos][capacity] != -1) return memo[pos][capacity];
int r1 = solve(pos + 1, capacity); //dont take
int r2 = 0;
if(weight[pos] <= capacity)
{
r2 = solve(pos + 1, capacity - weight[pos]) + profit[pos]; //take
}
return memo[pos][capacity] = max(r1, r2);
}
void reconstruct(int pos, int capacity)
{
if(pos == no_of_objects) return; //you have completed reconstruction
int r1 = memo[pos + 1][capacity]; //dont take
int r2 = 0;
if(weight[pos] <= capacity)
r2 = memo[pos + 1][capacity - weight[pos]] + profit[pos]; //take
if(r1 > r2)
{
reconstruct(pos + 1, capacity);
}
else
{
cout << "Take object " << pos << endl;
reconstruct(pos + 1, capacity - weight[pos]) + profit[pos];
}
}
执行 reconstruct 后,它将打印所有那些为您提供最佳解决方案的对象。如您所见,最多 no_of_objects 调用将在 reconstruct 函数中进行。
同样,你可以贪婪地重建任何DP的解决方案。
答案 3 :(得分:0)
大多数动态编程算法都使用Memoization和Backtracking。 Memoization是一种查找表,其中算法存储每个步骤的状态信息。算法完成后,它会使用回溯从算法的最后一个状态转到前一个状态。在knapsak上,这可以通过存储“我来自哪里?”来获得。值。如何计算M [i,w]?来自m [i-1.w]或m [i-1,w-wi] + vi。搜索Memoization和Backtracking以获得更多示例。