我有一张木板,并在木板上给了N标记。现在我必须切割木板上的所有标记,以便将所有标记的切割成本降至最低。现在假设我先切割第i个标记然后通过使用两个乘数a和b作为输入给出成本,成本是a *(左)+ b *(右),其中左和右是切割后木材剩余部分的大小。例如,如果我有一个长度为10,a = 3和b = 4的木材,如果我有ex:[1,3,5,7,10]的标记列表,所以我不能切割第一个和最后一个标记,因为它们是起点和终点木头所以假设如果我首先从标记7开始,那么切割成本将是3 *(7-1)+ 4 *(10-7)= 18 + 12 = 30现在我将拥有的木材是以标记开始的木材1到标记7,其他将是标记7到木材末端的标记,我将重复该过程,直到所有标记都被切割。
现在看完这个问题之后,我立即想到,为了以最低的成本砍伐木材我首先需要找到中点(切点的中位数),然后我应该切割木材并再次重复这个过程再次,直到木材没有切割点,但我在解决切割后获得的正确木材方面遇到问题
样品输入:用[1,3,5,9,16,22]切割的木材,当我们首先从中位数9开始时,最小成本= 163,那么我们将得到标记木材[1,3,5, 9]和[9,16,22]现在首先解决左边的木材[1,3,5] [5,9],现在再次切割我们有[1,3] [3,5] [5, [9,16,22]现在正在操作这种木材,我们已经切割了所有的标记,列表将是[1,3] [3,5] [5,9] [9,16] ] [16,22]并且此操作的成本最低
这是我的代码:
#parent {
display:grid;
grid-template-columns: repeat(3, 1fr);
grid-tempalte-rows: 1fr;
grid-column-gap: 10px;
}
#parent > * {
display: grid;
}
答案 0 :(得分:3)
首先,这是一个递归生成器solve_gen
,它检查所有可能的切割序列,然后选择最小切割序列。虽然代码是紧凑的,并且如果标记的数量很小,它运行正常,但随着标记数量的增加,它很快就会变得非常低效。我还添加了一个函数apply_cuts
,它将一系列剪切应用于标记序列,因此您可以按顺序查看剪切。
solve_gen
使用全局count
来跟踪所做的递归调用的数量。 count
对于算法的操作不是必需的,但是它给出了函数正在做多少工作的指示。
def cost(seq, m):
return (seq[-1] - seq[0]) * m
def solve_gen(seq):
global count
count += 1
if len(seq) == 2:
yield 0, ()
return
for i in range(1, len(seq)-1):
left, x, right = seq[:i+1], seq[i], seq[i:]
val = cost(left, a) + cost(right, b)
for lval, lcuts in solve_gen(left):
for rval, rcuts in solve_gen(right):
yield (val + lval + rval, (x,) + lcuts + rcuts)
def apply_cuts(seq, cuts):
total = 0
old = [seq]
for x in cuts:
new = []
for u in old:
if x in u:
i = u.index(x)
left, right = u[:i+1], u[i:]
val = cost(left, a) + cost(right, b)
new.extend((left, right))
else:
new.append(u)
print(x, new, val)
total += val
old = new[:]
return total
# Test
# Recursion counter
count = 0
a, b = 3, 4
seq = (1, 3, 5, 9, 16, 22)
print(seq)
results = list(solve_gen(seq))
val, cuts = min(results)
print('Cost: {}, Cuts: {}'.format(val, cuts))
print('Results length: {}, Count: {}'.format(len(results), count))
print('\nCutting sequence')
print(apply_cuts(seq, cuts))
<强>输出强>
(1, 3, 5, 9, 16, 22)
Cost: 163, Cuts: (9, 5, 3, 16)
Results length: 14, Count: 90
Cutting sequence
9 [(1, 3, 5, 9), (9, 16, 22)] 76
5 [(1, 3, 5), (5, 9), (9, 16, 22)] 28
3 [(1, 3), (3, 5), (5, 9), (9, 16, 22)] 14
16 [(1, 3), (3, 5), (5, 9), (9, 16), (16, 22)] 45
163
FWIW,以下是相同a
&amp;的结果。 b
标记序列较长。
(1, 3, 5, 9, 16, 22, 31, 33, 35, 39, 46)
Cost: 497, Cuts: (31, 16, 9, 5, 3, 22, 39, 35, 33)
Results length: 4862, Count: 41990
我们可以通过在递归的每个阶段找到最小值来提高效率,而不是找到所有可能性的最小值。然而,由于该算法研究了各种切割选项,因此它经常重复之前完成的计算。因此,我们可以通过使用缓存使代码更高效,即,我们将先前的结果存储在字典中,因此如果我们需要再次进行相同的剪切,我们可以在缓存中查找它而不是重新计算它。
我们可以编写自己的缓存,但functools
模块提供了lru_cache
,它可以用作装饰器。我们也可以给cost
函数一个缓存,虽然它的计算非常简单,因此缓存可能不会节省很多时间。 lru_cache
的一个很好的功能是它还可以提供缓存统计信息,这可以让我们知道缓存的有用性。
from functools import lru_cache
@lru_cache(None)
def cost(seq, m):
return (seq[-1] - seq[0]) * m
@lru_cache(None)
def solve(seq):
global count
count += 1
if len(seq) == 2:
return 0, ()
results = []
for i in range(1, len(seq)-1):
left, x, right = seq[:i+1], seq[i], seq[i:]
val = cost(left, a) + cost(right, b)
lval, lcuts = solve(left)
rval, rcuts = solve(right)
results.append((val + lval + rval, (x,) + lcuts + rcuts))
return min(results)
# Test
# Recursion counter
count = 0
a, b = 3, 4
seq = (1, 3, 5, 9, 16, 22)
print(seq)
val, cuts = solve(seq)
print('Cost: {}, Cuts: {}'.format(val, cuts))
print('Count: {}\n'.format(count))
print('cost cache', cost.cache_info())
print('solve cache', solve.cache_info())
<强>输出强>
(1, 3, 5, 9, 16, 22)
Cost: 163, Cuts: (9, 5, 3, 16)
Count: 15
cost cache CacheInfo(hits=20, misses=20, maxsize=None, currsize=20)
solve cache CacheInfo(hits=26, misses=15, maxsize=None, currsize=15)
幸运的是,我们得到的结果与以前相同。 ;)请注意,递归计数现在要低得多。让我们用更长的标记序列来尝试。
(1, 3, 5, 9, 16, 22, 31, 33, 35, 39, 46)
Cost: 497, Cuts: (31, 16, 9, 5, 3, 22, 39, 35, 33)
Count: 55
cost cache CacheInfo(hits=240, misses=90, maxsize=None, currsize=90)
solve cache CacheInfo(hits=276, misses=55, maxsize=None, currsize=55)
与之前的41990相比,递归计数仅为55;显着减少。我们可以看到缓存正在被广泛使用。
FWIW,所有可能的切割序列的数量由Catalan numbers给出,这通常会在组合问题中出现。
答案 1 :(得分:1)
这个问题需要函数和递归。你想要的是这样的:
function total_cost(a, b, marks):
# Do something clever here
现在你的标记少于3,问题很简单。不需要削减,成本为0。
function total_cost(a, b, marks):
if len(marks) < 3:
return 0
else
# Do something clever here
如果你有两个以上的标记,在任何特定地方切割的成本是在那里切割的成本,加上切割其余部分的成本。因此,切割成本是最小或那些成本。这应该足以填补一些聪明的东西。
此解决方案将以很多削减速度缓慢运行。要解决这个问题,你应该查找“memoization”。