【问题标题】:Prolog: How to break a piece of chocolate into its piecesProlog:如何将一块巧克力掰成碎片
【发布时间】:2015-01-15 03:31:03
【问题描述】:

我有以下任务要做:

给定一个由 m x n 个小矩形组成的矩形巧克力棒,并希望将其分成几部分。在每个步骤中,您只能选择一块并沿其任何垂直线或沿其水平线将其分解。你应该如何用最少的步数打破巧克力棒?

我知道你需要 m x n - 1 个步骤来打破巧克力棒,但我被要求用“CS 方式”来做:

定义一个谓词,该谓词在将巧克力棒分成小块的所有备选可能性中选择最少的步数。在一个额外的参数位置上构建一个结构,它告诉您在哪里以及如何打破条形以及如何处理生成的两个部分。

我的想法:在打破巧克力块一次后,你可以选择在垂直或水平线上打破它。所以这是我的代码,但它不起作用:

break_chocolate(Horizontal, Vertical, Minimum) :-
break_horizontal(Horizontal, Vertical, Min1),
break_vertical(Horizontal, Vertical, Min2),
Minimum is min(Min1, Min2).

break_horizontal(0,0,_).
break_vertical(0,0,_).

break_horizontal(0, V, Min) :-
    V > 0,
    break_horizontal(0, V, Min).

break_horizontal(H, V, Min) :-
    H1 is H-1,
    Min1 is Min + 1,
    break_vertical(H1, V, Min1).

break_horizontal(H, V, Min) :-
    H1 is H-1,
    Min1 is Min + 1,
    break_vertical(H1, V, Min).

break_vertical(H, V, Min) :-
    V1 is V-1,
    Min1 is Min + 1,
    break_horizontal(H, V1, Min1).

break_vertical(H, V, Min) :-
    V1 is V-1,
    Min1 is Min + 1,
    break_vertical(H, V1, Min1).

break_vertical(H, 0, Min) :-
    H > 0,
    break_horizontal(H, 0, Min).

谁能帮我解决这个问题?

【问题讨论】:

  • 我很久以前就忘记了 Prolog,但我会在 1x2 或 1x1 巧克力棒上测试它。我首先会怀疑无限递归。
  • “...但它不起作用”还不够好。究竟会发生什么?你期望会发生什么?您是否尝试过自己调试,发现了什么?
  • 正如@Boris 所指出的,对于n x m 条形图,断点数始终为mn - 1(通过归纳简单证明)。那么,你到底想让这个程序做什么呢?你能展示一个示例输出吗?
  • 我知道中断的次数总是 mn - 1,但我被要求为它编写一个程序。好吧,问题是 - 什么都没有发生。当我运行我的代码时,Prolog 只是冻结并且我的计算机崩溃了,所以我根本无法进行任何调试。
  • 您的代码存在一些问题。首先,使用break_horizontal(0, V, Min) :- V > 0, break_horizontal(0, V, Min). 进行无限递归修复,您的程序可能会停止崩溃。其次,整体逻辑仅假设您将X by Y 分成两块X-1 by Y1 by Y,或X by Y-1 和@ 987654333@ by 1 在很多地方。它可能会忽略一些您不只是中断一行或一列的情况。

标签: prolog logic


【解决方案1】:

这不是一个完整的答案,但应该将您推向正确的方向:

首先观察:每次您切一块巧克力棒时,您最终会得到比以前多一个块。所以,实际上,你没有“最少”的休息次数。您从 1 件(整个条形图)开始,以 m * n 件结束,因此您总是有准确的 m * n - 1 休息时间。所以要么你误解了你的问题陈述,要么在你的问题中以某种方式歪曲了它。

第二:一旦你分成两部分,你将不得不像打破前一个一样打破两部分中的每一个。一种编程方法是使用递归调用。就目前而言,我在您的程序中没有看到这一点。

第三:那么你想报告你的休息时间吗?你打算怎么做?

无论您是使用 Prolog、C 还是 JavaScript 进行编程,了解您的问题是找到解决方案的先决条件。

【讨论】:

    【解决方案2】:

    这里有一些用于表示和解决问题的额外提示。

    每次中断都会将一块分成两块(参见 Boris 的第二个提示)。因此,您可以将中断集合视为具有以下特征的中断二叉树:

    • 树的根节点的值为M-N(条的维度为M x N
    • 假设X-Y 表示树中任何节点的值,该值由Y 表示单个X 而不是单个片段1-1。由于节点的两个子节点表示维度XY 沿一维被破坏,因此X-Y 的子节点要么具有值A-YB-Y,其中A + B = X,要么值X-AX-B 其中A + B = Y
    • 树的所有叶节点的值都为1-1(可能的最小部分)

    二叉树的每个节点都由节点值、左子树和右子树组成。 An empty sub tree would have the value nil (or some other suitably chosen atom).树的常见表示形式类似于btree(X-Y, LeftSubTree, RightSubTree)(术语X-Y 是树的顶部节点的值,在这个问题中,将是相关块的尺寸)。例如,使用此方案,最小的一块糖果是btree(1-1, nil, nil)2 x 1 糖果棒的一组休息时间是 btree(2-1, btree(1-1, nil, nil), btree(1-1, nil, nil))

    您可以使用 CLPFD 库来约束 C #= A + BA #> 0B #> 0,并且为了消除对称情况,可以使用 A #=< B

    【讨论】:

    • 这是一种很好的表示方式。我在考虑剪辑的位置,这比你的建议提供的信息少。
    【解决方案3】:

    作为一种算法(我对 Prolog 不熟悉),我在中断次数上找不到任何不同的答案。我试过 4x4,但除了 15(高于或低于)之外,无法给出答案;我尝试了 5x2,但除了 9 之外无法找到答案。

    在此基础上,我建议最简单的编码方法:

    while there is more than one column:
        snap off the left-most column
        while this column has more than one square:
             snap off the top square
    while the remaining column has more than one square:
        snap off the top square
    

    根据情况,您可能希望更改以下一项或多项:(left, column)(top, row), left->right, top->bottom。

    【讨论】:

    • 使用这个算法会让加载变得更容易,但它并没有给我所有替代的可能性——这就是我真正苦苦挣扎的地方。
    • 啊,我误会问题了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-16
    • 1970-01-01
    • 2022-10-25
    • 1970-01-01
    • 2020-10-18
    相关资源
    最近更新 更多