我们需要按什么顺序在秤上称重?

In what order we need to put weights on scale?

正在做编程作业,不知道如何解决这个问题:

我们有一组 n 个权重,我们将它们一个一个地放在一个秤上,直到使用所有权重。我们还有一串 n 个字母 "R" 或 "L" 表示在那一刻哪支笔更重,它们不能保持平衡。不存在质量相同的砝码。计算我们必须按什么顺序将重量放在秤上以及放在哪个平底锅上。

目标是找到在秤上放置权重的顺序,因此输入字符串得到尊重。

输入:数字 0 < n < 51,权重数。然后是权重和字符串。

输出:在 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

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

既然你没有展示任何自己的代码,那么我将在没有代码的情况下给你一些想法。如果您需要更多帮助,请展示更多您的作品,然后我可以向您展示 Python 解决您问题的代码。

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

Backtracking is a general algorithm for finding all (or some) solutions to some computational problems, notably constraint satisfaction problems, that incrementally builds candidates to the solutions, and abandons a candidate ("backtracks") as soon as it determines that the candidate cannot possibly be completed to a valid solution.

Backtracking can be applied only for problems which admit the concept of a "partial candidate solution" and a relatively quick test of whether it can possibly be completed to a valid solution.

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

您的整个例程首先输入并准备数据。然后它调用一个递归例程,在每个级别选择一个重量和一个平底锅。每个级别所需的一些信息可以放入可变全局变量中,但如果将所有需要的信息作为参数传递会更清楚。每次调用递归例程需要传递:

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

递归的基本情况是 unused-weights 和 unused-letters 为空。然后您就完成了搜索并可以打印解决方案并退出程序。否则,您将遍历一个未使用的砝码和一个平底锅的所有组合。对于每个组合,计算如果将那个重量放在那个盘子上,新的不平衡会是多少。如果新的不平衡与相应的字母一致,则使用 appropriately-modified 参数递归调用例程。如果不是,请对此重量和平底锅不做任何操作。

在编码之前你还有一些选择要做,比如未使用权重的数据结构。向我展示一些您自己的编码成果,然后我会给您我的 Python 代码。

请注意,对于大量权重,这可能会很慢。对于 n 个砝码和两个盘子,将砝码放在盘子上的方式总数为 n! * 2**n(即阶乘和求幂)。对于超过 3e79n = 50,太大而无法完成。回溯避免了大多数选择组,因为选择会尽快被拒绝,但我的算法可能仍然很慢。可能有比回溯更好的算法,但我没有看到。你的问题好像是设计来回溯处理的。


既然你已经表现出了更多的努力,这是我的 un-optimized 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()

我认为加快该代码的主要方法是避免在内循环中调用 solve 时的 O(n) 操作。这意味着可能改变 unused_weights 的数据结构并改变它的方式,placement,也许 unused_tilts/later_tilts 被修改为使用 O(1) 操作。这些更改会使代码复杂化,这就是我没有这样做的原因。