我们需要按什么顺序来衡量权重?

时间:2019-06-07 22:49:58

标签: math combinatorics

我正在编程中做作业,但我不知道如何解决此问题:

我们有一组n个权重,我们将它们逐个缩放,直到使用了所有权重。我们还有n个字母的字符串“ R”或“ L”,这表示那一刻哪支笔较重,它们不能保持平衡。没有相同质量的砝码。计算我们必须按什么顺序将权重放在比例尺和平底锅上。

目标是找到按比例放置权重的顺序,以便尊重输入字符串。

输入:数字0

输出:在n行中,放置砝码,并在“ R”或“ L”侧放置砝码。如果有很多,则输出其中任何一个。

示例1:

输入:

3
10 20 30
LRL

输出:

10 L
20 R
30 L

示例2:

输入:

3
10 20 30
LLR

输出:

20 L
10 R
30 R

示例3:

输入:

5
10 20 30 40 50
LLLLR

输出:

50 L
10 L
20 R
30 R
40 R

我已经尝试使用递归来计算它,但是没有成功。有人可以帮我解决这个问题,还是给我提示如何解决。

1 个答案:

答案 0 :(得分:1)

由于您没有显示自己的任何代码,因此我将为您提供一些没有代码的想法。如果您需要更多帮助,请显示更多工作,然后我将向您显示可解决您问题的Python代码。

您的问题适合backtracking。维基百科对此算法的定义是

  

回溯是一种通用算法,用于查找某些computational problems,尤其是constraint satisfaction problems的所有(或某些)解决方案,该算法会逐步为解决方案构建候选,并尽快放弃候选(“回溯”)因为它确定候选人不可能完成有效的解决方案。

  

回溯仅适用于那些承认“部分候选解决方案”概念的问题,并且需要相对快速地测试它是否可以完成为有效的解决方案。

您的问题满足这些要求。在每个阶段,您需要选择剩余的砝码之一和秤的两个秤盘之一。将选定的砝码放在选定的秤盘上时,可以确定是否满足输入字符串中的相应字母。如果不是,则拒绝选择重量和平移。如果是这样,请继续选择另一个砝码和平移。

您的总体例行程序首先输入并准备数据。然后,它调用一个递归例程,该例程在每个级别选择一个权重和一个平移。每个级别所需的某些信息可以放入可变的全局变量中,但是如果您将所有所需的信息作为参数传递,则将更加清楚。每次调用递归例程都需要传递:

  • 尚未使用的砝码
  • 尚未使用的输入L / R字符串
  • 秤盘上砝码的当前状态,采用最终确定时可以轻松打印的格式(也许是砝码和秤盘的有序对数组)
  • 当前锅的重量失衡。可以从先前的参数计算得出,但是通过单独传递此参数可以节省时间。这将是右平底锅上的权重的总和减去左平底锅上的权重的总和(反之亦然)。

您的递归基本情况是未使用的权重和未使用的字母为空。然后,您已完成搜索,可以打印解决方案并退出程序。否则,您将遍历未使用的砝码之一和秤盘的所有组合。对于每种组合,请计算将重物放在该秤盘上会产生的新失衡。如果新的不平衡与相应的字母一致,请使用适当修改的参数递归调用例程。如果没有,则对此重量和平移不进行任何操作。

在编码之前,您还有一些选择要选择,例如未使用权重的数据结构。告诉我您自己的一些编码工作,然后我将为您提供Python代码。

请注意,对于许多重量来说,这可能会很慢。对于n权重和两个平底锅,将权重放置在平底锅上的方式总数为n! * 2**n(即阶乘和幂)。对于n = 50以上的3e79来说,太大了。回溯避免了大多数选择,因为选择会尽快被拒绝,但是我的算法可能仍然很慢。可能有比回溯更好的算法,但我看不到。您的问题似乎旨在通过回溯处理。


现在您已经付出了更多努力,这是我未优化的Python 3代码。尽管您为第三个示例提供了不同的有效解决方案,但该方法适用于您提供的所有示例。

def weights_on_pans():
    def solve(unused_weights, unused_tilts, placement, imbalance):
        """Place the weights on the scales using recursive
        backtracking. Return True if successful, False otherwise."""
        if not unused_weights:
            # Done: print the placement and note that we succeeded
            for weight, pan in placement:
                print(weight, 'L' if pan < 0 else 'R')
            return True  # success right now
        tilt, *later_tilts = unused_tilts
        for weight in unused_weights:
            for pan in (-1, 1):  # -1 means left, 1 means right
                new_imbalance = imbalance + pan * weight
                if new_imbalance * tilt > 0:  # both negative or both positive
                    # Continue searching since imbalance in proper direction
                    if solve(unused_weights - {weight},
                             later_tilts,
                             placement + [(weight, pan)],
                             new_imbalance):
                        return True  # success at a lower level
        return False  # not yet successful

    # Get the inputs from standard input. (This version has no validity checks)
    cnt_weights = int(input())
    weights = {int(item) for item in input().split()}
    letters = input()
    # Call the recursive routine with appropriate starting parameters.
    tilts = [(-1 if letter == 'L' else 1) for letter in letters]
    solve(weights, tilts, [], 0)

weights_on_pans()

我看到的加快代码速度的主要方法是避免在内循环中对O(n)的调用中进行solve操作。这意味着可能会更改unused_weights的数据结构,并更改placementunused_tilts / later_tilts的修改方式以使用O(1)操作。这些更改会使代码复杂化,这就是为什么我不这样做。