【问题标题】:How do I maximise the sum in this game?我如何在这个游戏中最大化总和?
【发布时间】:2015-09-12 11:32:20
【问题描述】:

所以,我在一次采访中被问到这个问题:

有两个朋友在玩一个游戏,他们从包含n 正数的数组中选择一个数字。两个朋友一次选择一个数字,两个玩家都以最佳方式玩游戏。并且您必须找出游戏结束后您可以获得的最大总和(正在选择的数字)是多少。在我没有限制地回答同一个问题后给出的限制是:

  1. 在双方玩家的第一步中,他们可以选择任何数字。
  2. 除了第一次移动外,他们只能选择与给定数组中的前一个数字相邻的数字,并且直到游戏中的那一刻之前第一个和第二个玩家都没有选择过。 (澄清为编辑)

如果玩家无法移动,他/她将停止游戏。当两个玩家都无法移动时,游戏结束。

现在,我给出的解决方案是:

  1. 创建一个结构,其中包含输入数组中的值以及值的索引。
  2. 将之前的结构创建一个数组,并将第一步的值存储在这个数组中。
  3. 根据值以非降序对该数组进行排序。
  4. 开始以贪婪的方式选择一个值并打印最大值。

尽管我也可以编写代码,但他们更多地在寻找伪代码。但是,面试官说这在某些情况下会失败。我想了很多关于哪些情况会失败但找不到任何情况。因此,我在这个问题上需要帮助。

另外,如果可能的话,请附上我可以做些什么来改进它的伪代码。

编辑:我想我的问题不够清楚,特别是第二点。面试官的意思是:

如果这不是玩家的第一个动作,他必须选择一个与他在之前的动作中已经选择的数字相邻的一个数字。

另外,是的,两位玩家都以最佳方式玩游戏,他们轮流选择数字。

Edit2:所以,我的朋友也问了同样的问题,但做了一点修改。他得到的不是一个数组,而是一个图表。所以,就像我的情况一样,我只能选择与我之前选择的索引相邻的索引,给他的是一个无向图(邻接表作为输入),他只能选择特定移动中的那些顶点直接连接到任何先前选择的顶点。

例如: 假设正整数的数量是 3。这些整数的值是 424,如果我将正整数命名为 ABC,那么,

A - B
B - C

上面是给我朋友的例子,上面的答案是6。你能指出我如何从这个开始的正确方向吗?谢谢!

【问题讨论】:

  • 您的解决方案当然是错误的。比如说,数组是:20 20 20 20 20 1 1 1 1 50(10 项)。你按非降序排序给你50 20 20 20 20 20 1 1 1 1。现在,你选择50开始,其他玩家选择20。现在,由于邻居选择限制,你只能选择1,而他总是可以选择20。最后,你输了,54-100。
  • 你必须找到被选中的数字的最大总和(由两个玩家)还是别的什么?
  • 如果都是正整数,那么肯定是最大和 = 数组中所有元素的总和,这发生在两个玩家从左右边界开始并朝着对方前进时。我认为还有更多。
  • @Dante 这里没有思维游戏。第一个玩家必须选择将数组平分的项目,两个结果数组的总和差小于选择的数字。如果可以实现,则 p1 获胜,否则,p2 获胜。
  • @PeterCordes 我认为对both friends select one number at a time 的最佳解释是他们轮流进行。否则他们都会在第一回合选择最好的物品。很明显,p2 的最佳第一步将紧挨着 p1。没有太多选择

标签: arrays algorithm


【解决方案1】:

请注意,如果您在索引x 处采取第一步,如果您的对手发挥最佳,他们的第一步将必须在索引x-1x+1 处。否则,他们将拥有可以选择但没有选择的元素。要看到这一点,请考虑两个不相邻的起点:

-------y-------------x-------

最终它们都会从数组中获取元素并最终得到类似的结果:

yyyyyyyyyyyyyyxxxxxxxxxxxxxxx

所以你可以将起点重新定位到中间yx,得到相同的解决方案。

因此,假设您首先移动到 x。让:

s_left_x = a[0] + ... + a[x]
s_right_x = a[x] + ... a[n - 1]

s_left_y = a[0] + ... + a[x - 1]
s_right_y = a[x + 1] + ... + a[n - 1]

假设您想赢得比赛:在最后获得比您的对手更大的金额。如果你的对手选择x + 1,你想要s_left_x > s_right_y,如果你的对手选择x - 1,你想要s_right_x > s_left_y。理想情况下,这是为了获胜。不过,并非总是有可能赢,而且您的问题不是问如何赢,而是问如何获得最大的金额。

因为你的对手会发挥最佳,他会迫使你进入最坏的情况。所以对于每一个x作为你的第一步,你能做的最好的就是min(s_left_x, s_right_x)。为每个索引x 选择此表达式的最大值,经过一些预计算后,您可以在O(1) 中找到每个索引的最大值。

【讨论】:

  • 我猜你假设数组是排序的。事实并非如此。另外,请检查我的编辑一次问题。
  • @JohnDoe 不,我不认为数组已排序。如果您指出它看起来那样,我会尝试澄清。但我现在看到,该数字可以与迄今为止选择的任何数字相邻......稍后将进行编辑以澄清或删除。
  • 我认为您可以一次找到平衡左右和的元素,而不是预先计算。跟踪两个位置,从数组的开头和结尾开始。向内工作,保持总和〜均匀。
  • @JohnDoe 我已经更新了我的答案。我认为您的编辑实际上并没有改变解决方案。如果您有任何问题,请告诉我。
  • @IVlad,非常感谢您的大力帮助。然而,当我的朋友以不同的格式提出同样的问题时,我又多了一个疑问。请查看我的 Edit2 并让我知道如何继续进行?再次非常感谢。
【解决方案2】:

好的,我认为这是解决方案,表述得更简洁:

第一个玩家必须选择将数组平分的项目,并且两个结果数组的总和差异小于选择的项目的值。

如果可以实现,则 p1 获胜,否则,p2 获胜。

显然,p2 在他的第 1 步必须选择 p1 旁边的项目,因为这是他获得最大总和的唯一方法。他选择旁边的项目,剩余项目的总和更大。这也是 p2 可以获得的最大金额。

p1 的最大总和将是剩余项目的总和(在边上的项目,p2 没有选择的项目加上第一个动作中选择的项目 p1)。

【讨论】:

    【解决方案3】:

    正如 OP 提到的两个玩家都以最佳方式玩游戏,我将在此假设下提出一个算法。

    如果两个玩家都打得最好,那么他们最后得到的总和肯定是最大的,否则他们就不是打得最好。

    这里有两种不同的情况:

    我迈出第一步,在 x 位置选取元素

    现在因为我们必须遵守只能选择相邻元素的条件,所以让我在这里定义两个数组。

    left[x]:是元素相加得到的总和

    array[0],array[1]....array[x-1],the elements left to x.
    

    right[x]:是元素相加得到的总和

    array[x+1],array[x+2]....array[n-1],the elements right to x.
    

    现在,因为其他玩家也打得最好,他会做的是检查我可能达到的目标,他发现,我可以达到以下目标:

    array[x] + left[x] = S1
    

    array[x] + right[x] = S2
    

    所以其他玩家所做的就是找到 S1 和 S2 的最小值。

    如果 S1

    如果 S1 > S2 这意味着如果另一个玩家在 x-1 处选择元素,他只是从我们那里拿走了数组的大部分,因为现在我们只剩下一个较小的和 S2

    因为我也打得最好,所以我会在第一步中选择绝对值最小为 (right[x]-left[x]) 的 x,这样即使我们的对手占据了更好的部分我们的阵法,他只能带走最少的

    因此,如果两个玩家都发挥最佳,则获得的最大总和为:

    更新。

    x + left[x]  and right[x]//when second player in his first move picks x+1
    

    因此,在这种情况下,采取的行动是:

    Player 1:Picks element at position x,x-1,x-2....0.
    
    Player 2:Picks element at position x+1,x+2,....n-1
    

    因为每个玩家都必须选择与他之前选择的元素相邻的元素。

    x + right[x] and left[x]//when second player in his first move picks x-1
    

    因此,在这种情况下,采取的行动是:

    Player 1:Picks element at position x,x+1,x+2....n-1.
    
    Player 2:Picks element at position x-1,x-2,....0.
    

    因为每个玩家都必须选择与他之前选择的元素相邻的元素。

    其中 x 使得我们获得 (right[x]-left[x]) 的最小绝对值。

    由于OP坚持发布伪代码,这里有一个:

    计算左右数组。

    for(i = 0 to n-1)
    {
     if(i==0)
      left[i]=0;
     else left[i] = left[i] + array[i-1];
    
     j = n-1-i;
     if(j==n-1)
      right[j]=0;
     else right[j]= right[j] + array[j+1];
    }
    

    左右数组的初始位置都是0。

    计算 max_sums。

    Find_the_max_sums()
    {
     min = absoulte_value_of(right[0]-left[0])
     x = 0;
     for(i = 1 to n-1)
     {
      if( absolute_value_of(right[i]-left[i]) < min)
      {
       min = absolute_value_of(right[i]-left[i]); 
       x=i;
      }
     }
    }
    

    显然,该算法的空间复杂度和时间复杂度都是线性的。

    【讨论】:

    • 您的最后一个函数将只计算第一步的值。我如何在整个游戏中找到它?我想要整个游戏的最大总和。
    • @JohnDoe 我的最后一个函数确定出现最大值的 x 的值。你说得对,它与先手有关,但最大和是由先手决定的。
    • @JohnDoe 最大金额有两种可能性,我在回答中已经明确提到过。
    • @JohnDoe 最后一个函数会给我们分区的位置,这样数组被分成最大和的两部分(根据游戏规则)。
    • @JohnDoe 如有任何疑问。
    【解决方案4】:

    为了完成@Ivlad 的回答,存在一种p1 永远不会输的策略(在最坏的情况下可能会出现平局)。她总能找到一个x,这样她得到的总和不小于p2的总和。

    证明如下 - 对不起,编码更多的数学。我考虑一个正数数组(直到一个恒定的平移,这里不失一般性)。

    我表示a = [a[0],...,a[n]] 正在考虑的数组,对于任何k&lt;=lS[k,l] = a[k]+...+a[l],所有术语的总和从排名k 到排名l。按照惯例,S[k,l]=0 如果k&gt;l

    正如在之前的 cmets 中所解释的,在以下情况下选择 a[k+1] 可以保证 p1 获胜:

    a[k+1] >= max( S[1,k]-S[k+2,n] , S[k+2,n]-S[1,k] )
    

    让我们考虑D[k] = S[1,k] - S[k+1,n],即第一个k 项之和与其他项之和之间的差异。序列D 递减,k=0 为负数,k=n 为正数。我们可以索引i,这样:

    D[i] <= 0
    D[i+1] => 0
    

    (其实上面两个不等式之一一定是严格的)。

    替换S 并注意到S[1,k+1]=S[1,k] + a[k+1]S[k+1,n]=S[k+2,n] + a[k+1]D 中的两个不等式意味着:

    S[1,i] <= S[i+2,n] + a[i+1]
    S[1,i] + a[i+1] >= S[i+2,n]
    

    或等效:

    a[i+1] >= S[1,i] - S[i+2,n]
    a[i+1] >= S[i+2,n] - S[1,i]
    

    也就是说,选择a[i+1]是p1不会输的策略。

    事实上,这种策略为 p1 提供了最大的回报。请注意,即使数组的所有项都是严格正数,它也不是唯一的。以a=[1,2,3] 为例,其中 p1 可以无差别地选择 2 或 3。

    【讨论】:

      【解决方案5】:

      鉴于您的限制,任何游戏中的最大总和将始终是从给定分割位置 S 到位置 1 或位置 N 的多个相邻值的相加。玩家 1 选择初始分割点 S,玩家 2选择他在数组中的一侧进行求和(因此玩家 2 也选择玩家 1 的一侧)。一位玩家将 1 加到 S(或 S-1),另一位玩家将 S(或 S+1)加到 N。总和较高的获胜。

      为了赢得比赛,玩家 1 必须找到一个分裂位置 S,使得从 1 到 S-1 和从 S+1 到 N 的加法都严格小于对方的总和加上 S 的值. 这样无论玩家 2 选择添加哪一方,其总和都会更小。

      伪代码:

      1. For each position S from 1 to N, repeat:
      1.1. Add values from 1 to S-1 (set to zero if S is 1), assign to S1.
      1.2. Add values from S+1 to N (set to zero if S is N), assign to SN.
      1.3. If S1 is smaller than S+SN and SN is smaller than S+S1, then S is the winning position for Player 1. If not, repeat.
      2. If you have found no winning position, then whatever you choose Player 2 can win choosing in turn an optimal position.
      

      【讨论】:

        最近更新 更多