我需要一个提示来开始这个编程难题

2022-08-30 17:17:16

你有一个充满平衡和重量的房间。每枚天平重达 10 磅,当左右两侧的重量总和完全相同时,天平被认为是完全平衡的。您为某些余额放置了一些权重,并将一些余额放在其他余额上。根据天平的排列方式以及每个天平的额外重量的描述,确定如何向天平添加权重,以便它们完全平衡。

平衡一切的方法可能不止一种,但始终选择对最低余额施加额外权重的方式。

输入文件将以单个整数 N 开头,指定有多少余额。余额 0 由第 1 行和第 2 行指定,余额 1 由第 3 行和第 4 行指定,依此类推...每对行的格式如下所示:

WL <balances>
WR <balances>

WL 和 WR 分别表示左侧和右侧的重量。是位于此天平那一侧的其他天平的空格分隔列表。可能包含零个或多个元素。

请考虑以下输入:

4
0 1
0 2
0
0 3
3
0
0
0

Balance 0 has balance 1 on its left side and balance 2 on its right side
Balance 1 has balance 3 on its right side
Balance 2 has three pounds on its left side
Balance 3 has nothing on it

由于天平3上没有任何东西,它已经完全平衡,总重量为10磅。Balance 2 没有其他平衡,所以我们所需要做的就是通过在右侧放置三磅来平衡它。现在它总共重达16磅。天平1的右侧有天平3,重10磅,所以我们只把10磅放在它的左边。天平 1 的总重量为 30 磅。余额0的左侧有余额1(30磅),右侧有余额2(16磅),我们可以通过在右侧添加14磅来平衡它。

输出应为 N 行长,第 n 行列出添加到第 n 个余额的权重,格式如下:

<index>: <weight added to left side> <weight added to right side>

因此,此问题的输出将是:

0: 0 14
1: 10 0
2: 0 3
3: 0 0

我试过了,但我想我真的不擅长编程。我应该从哪里开始?请不要发布解决方案;我想学习。


答案 1

这是你的树

           (0)
     -------------      
     |           | 
    (2)         (1)
 ---------    -------   
 |       |    |     |
[3p]     ..   ..   (3)
                  ------
                  |     |
                 ...    ..

您的逻辑应在内存中创建此树,其中每个节点都包含以下数据结构。

BalanceIdx: Integer;    //The balance index number if any. -1 indicates none
InitialPounds: Integer; //Initial weight in the node 
AddedPounds: Integer;   //The weight you added when processing. Default is 0
TotalWeight: Integer;   //**Total weight of a node including all its children. This is also our indication that a particular branch is already traversed. Default is -1

我们正在谈论一个递归函数,它基本上知道它在树的任何节点中只有两条路径或没有路径可遵循。每个递归都被视为坐在天平的盘子上。

这是逻辑。

  1. 从根开始,一直行进,直到找到一个没有路径可走的节点。

  2. 在它的帮助下更新它。TotalWeightInitialPounds

  3. 现在查看节点的其他同级节点是否已设置。如果为 NO,则将递归函数的根设置在那里并执行。TotalWeight

  4. 如果,计算差异并更新您坐的位置。现在转到父级并更新其.(不要忘记为余额添加10p)。然后去找祖父母并重复3。AddedPoundsTotalWeight

递归函数完成遍历整个树后,您就在每个节点中进行了记录。使用另一个递归函数来构造输出。AddedPounds

这个答案是为您要求的初学者准备的。


答案 2

剧透警告:包括完整的解决方案(读取输入除外)

这个问题很古老,看起来很有趣,所以我只是在几个类似提示的开头段落之后去解决了它(在C中,而不是像这样被标记的PHP)。我使用了冗长的变量名称并进行了注释,因此它应该作为伪代码工作。我打算写类似C的伪代码,但从来没有遇到过任何值得总结的东西。


此问题的主要复杂之处在于您不能使用二叉树,因为单个天平可以在其中一个或两个平移上有多个其他天平。这里最简单的方法可能是节点的链接列表,其中每个节点都有左子节点和右子节点,还有一个同级指针。若要获取位于余额左平移上的所有余额,请遍历同级指针的链接列表,从您正在查看的余额的节点的左侧子级开始。

由于输入格式为所有余额分配数字,因此最简单的方法是将这些索引用于结构数组。如果可以假设余额少于 2^31,则可以使用 32 位 int 而不是 64 位指针来节省内存。(负索引是空的哨兵,就像基于指针的树/列表实现中的 NULL 指针一样)

struct balance {
    // weights sitting directly on the pans of this balance
    float weight_left, weight_right;  // or unsigned int; the question only said the balance-count was an int.  I assumed the balance-IDs were int, too

    // optionally: float adjustment;  // For recording our changes.  negative or positive for adding to left or right balance.

    // references to other balances: negative means empty-list, otherwise index into an array.
    int next;           // linked-list of sibling balances on the same pan of a parent
    int left, right;    // list-heads for balances on the left and right pan
};

当他们说你应该给“最低”的余额增加重量时,我猜他们的意思是根在底部。它不是一棵悬挂在某物上的悬空天平树。

您可以向已携带天平的天平添加重量。因此,在树叶的空平底锅上增加重量并不复杂。(这需要以一种保持每个子树单独平衡的方式划分权重)。

因此,以递归方式解决这看起来非常简单。

  • 子树可以单独平衡,而无需有关任何其他余额的任何信息。
  • 砝码和其他天平平衡的组合构成左右平底锅的负载并不重要。你只需要这两个总数。通过将重量直接添加到较轻的平底锅中来平衡。
  • 在平衡其所有子树平衡余额。您可以在平衡子树的同时将子树的权重相加。

所以算法是:

static const float BALANCE_WEIGHT = 10.0f;

// return total weight of the balance and everything on it, including the 10.0f that this balance weighs
// modify the .weight_left or .weight_right of every node that needs it, in the subtrees of this node
float balance(struct balance storage[], int current)
{
    float lweight = 0, rweight = 0;

    // C++ can make this more readable:
    // struct balance &cur = storage[current];

    // loop over all the left and then right children, totalling them up
    // depth-first search, since we recurse/descend before doing anything else with the current
    for (int i = storage[current].left ; i >= 0 ; i = storage[i].next )
        lweight += balance(storage, i);

    for (int i = storage[current].right; i >= 0 ; i = storage[i].next )
        rweight += balance(storage, i);


    lweight += storage[current].weight_left;
    rweight += storage[current].weight_right;

    float correction = fabsf(rweight - lweight);

    // modify the data structure in-place with the correction.
    // TODO: print, or add to some other data structure to record the changes
    if (lweight < rweight) {
        storage[current].weight_left += correction;
    } else {
        storage[current].weight_right += correction;
    }

    return BALANCE_WEIGHT + rweight + lweight + correction;
}

要记录您所做的更改,请在数据结构中使用额外的字段,或者在从深度优先平衡中恢复时销毁原始数据(因为不再需要它)。例如,如果左侧需要添加重量,则存储,否则反其道而行之。.weight_left = correction; .weight_right = 0;


如果存在全局数组,则此实现将使用较少的堆栈空间,而不是每个递归调用都必须传递指针。这是一个额外的值,必须在寄存器调用ABI中保存/恢复,并在堆栈调用ABI(如32bit x86)中直接占用额外空间。storage

所有涉及当前节点的计算都发生在最后,而不是在开始时读取它们,然后必须重新读取它们以进行+=读取 - 修改 - 写入。编译器无法进行此优化,因为它无法知道数据结构没有周期,从而导致修改其中一个权重。weight_leftweight_rightbalance(subtree)

出于某种原因,x86 gcc 5.2 将其编译为非常庞大的代码。它更理智与. 确实没问题,但错过了一些优化。-O3-O2clang-O3


推荐