在锦标赛中获得分数的最佳算法(使用演示2玩家游戏)

时间:2014-08-26 10:44:55

标签: javascript c++ algorithm

背景

我在完成的CodeForces竞赛中遇到了this problem。这个问题被称为“一个关于树木的简单问题”。

  

Pieguy和Piegirl正在玩游戏。它们有一个带根的二叉树,它有一个属性,每个节点都是一个叶子或者只有两个子节点。每片叶子都有一个与之相关的数字。

     

在他/她的回合中,玩家可以选择任意两个分享他们的直接父母的叶子,移除他们,并将他们的任何一个值与他们的父亲相关联,现在它变成一片叶子(玩家决定将两个值中的哪一个关联起来) )。当只剩下一个节点(树的根)时,游戏结束。

     

Pieguy排在第一位,他的目标是在游戏结束时最大化与根相关联的价值。 Piegirl希望尽量减少这个价值。假设两个玩家都在以最佳方式进行游戏,那么当游戏结束时,与根相关联的数字会是多少?

树的大小最多为250个节点。

比赛中没有人解决了这个问题。

问题

什么是解决此问题的有效算法?

我会对C ++(可以在CodeForces网站上测试)或Javascript(这将允许我在游戏中添加AI)的答案感兴趣。

我尝试了什么

通过选择阈值水平T并回答“can piegirl保证得到小于或等于T的值?”的问题,可以简化问题。 如果我们可以解决这个更简单的问题,那么我们可以使用二分法来找到T的最小值,这将是原始问题的答案。

简化的问题相当于一个带blob的游戏: enter image description here

规则:

  1. 这是一款双人游戏,游戏的目的是加入所有的blob,制作一个你的颜色的巨大斑点。
  2. 玩家轮流组合blob。
  3. 您可以组合两个相同大小的相邻blob。如果两种斑点中的任何一种都是你的颜色,那么最终颜色将是你的颜色。
  4. 我制作了这个游戏here的演示,试图了解策略。

    到目前为止,感觉就像:

    1. 制作你的颜色大斑点是好的
    2. 经常会有一个陷阱,其中有一个移动的人将丢失
    3. 最后一次移动的玩家只需要让前两个子游戏中的一个有颜色,而另一个玩家需要让两个子游戏都有颜色
    4. 通常会有许多等待移动的区域,这些移动不会影响子画面的最终颜色。
    5. 我认为一个简单的极小极大前瞻(例如,评估函数可以将较高的blob评分得更高)在实践中可能运行良好,但感觉应该有一个更好的算法可以最佳地解决这个问题。

      任何人都有进一步的想法吗?

      更新

      我在演示中添加了一个minimax解算器(单击FindBest使计算机发挥作用)。 这适用于最深4的深度,以毫秒为单位求解,但需要很长时间才能考虑深度5及以上。我可以通过保存以前看到的位置的结果来加速这一点,但即使有了这种改进,它仍然会有一个巨大的状态空间来探索。

2 个答案:

答案 0 :(得分:1)

这不是一个完整的答案,但是评论的时间太长了。

让我们称球员为蓝色和红色。对于给定的树,在最佳游戏下获胜者有四种可能:总是蓝色(B),总是红色(R),总是第一个玩家(1),总是第二个玩家(2)。我们称之为子树即使它有一个奇数的叶子(即偶数个移动)和Odd,如果它有一个偶数叶子(即奇数个移动)。

以下是一个案例分析,遗漏了几个案例。它仍然可能有助于优化极小极大搜索。

子树的三种质量上不同的可能性是偶数 - 偶数,偶数 - 奇数(对称,奇数 - 偶数)和奇数 - 奇数。让我们假设蓝色首先对称。

偶 - 偶

蓝色首先和最后一个。如果有B1子树,则Blue会通过播放来获胜。随后通过Blue echo Red的播放选择子树。如果两个子树都是R2,则Red会通过回显Blue的子树选择来获胜。

Missing cases: none

奇 - 奇

蓝色首先和最后一个。如果有B2子树,那么Blue会通过在另一个中进行游戏来获胜,并且只要Red执行就会在那里停止。如果两个子树都是R,则Red通过在两个子树中强制交替来获胜。

Missing cases: R1 (= 1R), 11

偶 - 奇

蓝色首先发挥,红色发挥最后。如果奇数子树是R2,那么只要Blue执行,Red就会在偶数子树中停止。如果偶数子树为B2,而奇数子树为B1,则Blue会通过在奇数子树中进行游戏并响应Red的后续播放来获胜相同的子树。

Missing cases: RB, R1, 1B, 11

评论

有问题的案例似乎是一个玩家强迫对方在子树中连续两次移动的案例。从比赛参数看来,在一个任意移动之后有足够的时间来确定赢/输,但是案例分析变得非常长,比我现在耐心的时间长。

答案 1 :(得分:0)

更新

该解决方案已发布在教程here中:

  

此问题的解决方案是在树上进行动态编程   O(n)复杂性。

     

在这篇社论中,“偶数树”是指玩家将在其中制作的树   偶数个转弯,而“奇怪的树”是玩家的树   会做出奇数个转弯。

     

我们将解决一个稍微修改过的问题:其中一个问题   叶子上的数字是$ 0 $ s和$ 1 $ s。一旦这个问题得到解决,   一般问题可以通过二进制搜索来解决   回答,然后将所有叶子标记为具有更高或相等的值   $ 1 $ s,所有其他值为$ 0 $ s。

     

如果树是奇怪的树,那么第一个玩家进行最后一次转弯,   在那一刻只有两个孩子中的一个就足够了   根是1.如果树是偶数树,那么第二个玩家   最后一个回合,所以对于第一个玩家来说,至关重要   那个时候,如果他想赢的话,这两个孩子都是1。

     

一个简单的例子是当树是一棵奇怪的树时,它的两个都是   直接子树甚至是树(通过直接子树,或者只是   节点的“子树”,这里我们将指的是一个以其中一个为根的子树   节点'直接的孩子们。)

     

在这种情况下,我们可以递归地解决每个直接子树,   如果第一个玩家赢得其中任何一个,他就会赢得整棵树。他   这是通过让他第一次进入他能赢的树,而且   然后每次第二个玩家在那棵树上转弯,   用相应的最佳动作回应,每次都有   第二个玩家在另一个树上转弯,随机移动   那里。

     

然而,如果两个直接子树都是奇数树,则类似的逻辑   不管用。如果第二个玩家看到第一个玩家可以获胜   其中一棵树,第一个玩家已经转过来了   树,第二个玩家可以强迫第一个玩家玩   其他树,其中第二个玩家将在最后一个转弯之后   第一个玩家将被迫在第一个转弯   树,有效地让自己连续两次转弯。所以   赢得比赛即使第一名球员也能赢得一棵树   第二个玩家可以选择跳过一个回合。

     

因此,我们需要动态编程解决方案的第二个维度   这将表明其中一名球员是否可以跳过一回合   (我们称这两个州是“canskip”,如果一个人可以跳过一个转弯和“noskip”   如果没有这样的选择)。它可以很容易地显示出来,我们   不需要存储可以跳过多少圈,因为如果两次转弯   可以跳过,它有利于一个玩家跳过一个转弯,另一个   玩家将立即使用另一个跳过,有效地跳过   无用的。

     

为了使术语更容易,我们将使用术语“我们”来描述   第一个玩家,“他”描述第二个玩家。 “我们可以赢   一个子树“意味着我们可以赢得它,如果我们先去那里,”他可以获胜   一个子树“意味着如果他先行,他就能赢得它(所以”如果有的话   首先“总是假设并省略”。如果我们想说“我们可以   赢得第二名“,我们将改为说”他不能赢得[先行]“   或者“他失去了[先走了]”,这具有相同的含义

     

现在我们需要考虑六个案例(三个可能的孩子   乘以是否可以跳过转弯)。在所有情况下我们   假设两个孩子都剩下至少两个回合。案例   当一个孩子没有左转时(它是一个叶子节点),或者当它没有   只剩下一个回合(它是一个两个孩子都是叶子的节点)   两个角落的情况都需要单独处理。也是   重要的是要注意,当一个人开始处理那些角落案件时,   当球员必须跳过一个时,他会遇到一个额外的状态   转过来,即使这对任何被迫做的人都没有好处   那。我们称这种状态为“forcekip”。在两个子树的情况下   有一个以上左转,forcekip和canskip是相同的,   因为玩家总是同意以这种方式玩,即跳过,   如果可用,使用,而不改变结果。我们只在下面   描述canskip和noskip案例,从过渡的角度来描述   canskip和noskip状态。然而,人们需要介绍   当他处理角落情况时,强制状态,我们没有描述   在这篇社论中。 forcekip的答案与   在一般情况下回答跳过,但对于角落情况不同。

     

even-even-noskip:最简单的情况,如上所述,这就足够了   我们赢得任何没有跳过的子树。

     

even-even-canskip:这种情况类似于有一个奇数的情况   子树和一个偶数子树,并且没有跳过(跳过可以   只是被视为附加到其中一棵树上的额外转弯),所以   转换类似于所描述的奇偶偶数情况   下面。我们赢了,如果我们能用canskip赢得一棵树,他就赢了   另一个是noskip。

     

odd-even-noskip:如果我们能够在没有跳过的情况下赢得奇怪的树,那么他   没有跳过就无法赢得偶数树,那么我们就转向了   奇怪的树,并把它带入偶数 - 偶数noskip案件,他失去了   两棵树,所以我们赢了。另一个不那么微不足道的条件   我们赢了就是如果我们能用canskip赢得平坦的树,他就无法获胜   与canskip奇怪的树。这种情况的动机是奇怪的   带跳过的子树类似于偶数子树,所以通过转弯   在偶数情况下,我们将对手带到一个奇怪的情况,他在那里   用跳过丢失三个三分,这意味着无论哪一棵树   他转过身来,我们将回应那棵树,即便如此   他用另一棵树做了一个跳,他仍然会把树丢进去   他第一次转弯。由于我们做了最后一步,我们赢了。

     

odd-even-skip:这是一个简单的案例。我们可以将跳过视为一个   在奇数子树中额外转弯,只要我们能赢得甚至子树   没有跳过,或者带有跳过的奇数子树,我们就赢了。

     奇数奇怪的noskip:我们需要通过跳过来赢得任何一个子树   取胜。

     奇怪 - 奇怪 - 跳过:处理这种情况我们可以先立即考虑   跳过:如果他丢失当前子树的noskip案例,那么我们   赢得。否则我们赢了,如果我们可以用跳过赢得一棵树,他就是   没有跳过就无法赢得对方。

     

每个案例的更详细的动机保留为   锻炼。