【问题标题】:Interviewstreet- Permutation GameInterviewstreet- 排列游戏
【发布时间】:2012-04-02 09:53:35
【问题描述】:

爱丽丝和鲍勃玩以下游戏:

1) 他们选择前 N 个数字的排列开始。

2) 他们交替演奏,爱丽丝先演奏。

3) 在一个回合中,他们可以从排列中删除任何一个剩余的数字。

4) 当剩余的数字形成递增序列时,游戏结束。玩最后一回合(之后顺序增加)的人赢得了游戏。

假设双方都发挥最佳,谁会赢得比赛?

输入: 第一行包含测试用例 T 的数量。接下来是 T 测试用例。每个案例在第一行包含一个整数 N,然后在第二行包含整数 1..N 的排列。

输出: 输出 T 行,每个测试用例一个,如果 Alice 获胜则包含“Alice”,否则包含“Bob”。

示例输入:

2

3

1 3 2

5

5 3 2 1 4

样本输出:

爱丽丝

鲍勃

约束:

1

2

排列最初不会是递增序列。

我正在尝试解决上述问题。我已经推导出来,但我被困在一个点上。请帮助我继续进行。

在上述问题中,对于长度为 2 的排列,玩家 1 总是获胜。

对于长度为 3 的排列,如果字符串严格增加或减少,则玩家 2 获胜。

对于长度为 4 的排列,如果玩家 1 能够通过删除一个字符使字符串严格增加或减少,则她获胜,否则玩家 2 获胜。

因此得出的结论是:

如果当前玩家能够使字符串严格增加他/她获胜。 (小案例)

如果他/她能够使其严格递减,则获胜者将由该序列中的元素数量决定。如果该序列中有偶数个元素,则当前玩家输,否则为赢。

但是如果结果字符串既不增加也不减少怎么办?

【问题讨论】:

  • 这是作业吗?问作业问题不是坏事,但如果这是作业,请标记它。
  • 我没有什么可以给你的建议,但是对于这类问题,可能值得查看 "Games of No Chance""More Games of No Chance" 的书籍,它们处理这种“完整信息”游戏。这些书包含许多关于谁能赢得这样的两个玩家游戏的证据,以及在什么条件下,所以如果没有别的,它们可能值得寻找灵感。
  • 这个问题需要算法游戏的基础知识。您可以在Topcoder 上阅读有关它的信息。对于这个问题,我认为您可能还需要使用 memoizaion。希望对您有所帮助。
  • @Li-aungYip 感谢您的回复。我无法弄清楚问题是否是家庭作业问题!如何?我试图解决它,但无法解决,所以决定寻求帮助。 :) 感谢您提到的宝贵资源。 :)
  • @PriyankBhatnagar 我已经完成了那个教程!不管怎么说,还是要谢谢你。 :)

标签: algorithm math game-theory


【解决方案1】:

这是一个典型的游戏问题。您有 2^15 个可能的位置,表示剩余的数字。从剩余数字的数量中,您可以得出轮到谁了。所以现在你有一个以下列方式定义的图 - 顶点是剩余数字的可能集合,并且有一条边连接两个顶点 u 和 v 如果有一个移动将集合 u 更改为集合 v(即集合 v正好少一个数)。

现在您已经指出了哪些位置您马上就知道谁是赢家 - 那些代表数字递增序列的位置被标记为输掉。对于所有其他位置,您可以通过以下方式确定它们是赢还是输:如果有一条边连接到亏损位置,则该位置是赢的。所以剩下的就是像带有记忆的dfs这样的东西,你可以确定哪些位置是赢的,哪些是输的。由于图形相对较小(2^15 个顶点),此解决方案应该足够快。

希望这会有所帮助。

【讨论】:

  • 谢谢!!我会尝试按照您提到的方式进行操作,尽管它与我的想法不同。如果我在我的方法中得出任何结论,我会在这里发布作为答案。 :)
  • 注意剩余数字集的数量(远小于排列的数量)是一个有用的观察。不过,我建议基于图形的解决方案可能过于复杂。相反,我会简单地跟踪剩余数字的每个组合,是否存在获胜的举动。如果(1)其余数字不按顺序排列,并且(2)至少存在一个剩余数字,因此删除它会使对手处于失败位置,则一组数字被视为获胜。
  • 如果一个人检查所有剩下两个数字的组合,然后是三个,然后是四个,等等,最终会找到所有数字都剩下的组合。如果该组合存在获胜的举动,则第一个玩家获胜。否则第二个玩家获胜。
【解决方案2】:

当然,这可以通过对小 N 的“蛮力”来完成,但您不怀疑inversionsthe sign of a permutation 的答案更简单吗?

最初我怀疑“如果符号为 -1,Alice 获胜,否则失败”之类的答案,但事实并非如此。

但我想提出一个问题的表示,不仅您的算法可以使用,而且同样可以提高您在游戏中的纸笔性能。

反转是一对索引 ia[j]。考虑 (i,j) 一个顶点为 1,...,N 的无向图的边。每个玩家从该图中删除一个顶点,如果没有剩余边则获胜。

对于5 3 2 1 4,结果图是

   5--3
  /|\ |
 / | \|
4  1--2

Alice 很快发现删除“5”给了 Bob 删除 2 的机会。然后没有反转,Bob 获胜。

【讨论】:

    【解决方案3】:

    这个游戏可以递归解决。

    每次爱丽丝第一次选择并选择 i 时,从所有剩余的大于 i 的数字中减去 1。现在我们有同样的游戏,但数字是 1 到 N-1

    假设你的序列是

    1,3,5,4,2

    在她的第一步,爱丽丝可以选择任何数字。 情况1: 她选了 1,如果 bob 不能用 3,5,4,2 取胜(相当于 2,4,3,1),alice 可以取胜

    案例2: 她先选了3个。如果 bob 不能以 1,5,4,2 获胜(相当于 1,4,3,2),则 Alice 可以获胜

    案例3: 她先选了5个。如果 Bob 不能用 1,3,4,2 获胜,Alice 可以获胜

    你明白了。

    因此,您可以通过对每个可能的第一次猜测使用大小为 N-1 的排列来创建一个递归函数来计算大小为 N 的排列的解决方案。递归的基本情况是当你有一个有序序列时。

    递归的每一步,这个人都会尝试所有的可能性,并选择任何让他们获胜的可能性。

    因为有很多组合的移动可以归结为相同的序列,所以递归存在重叠子问题。这意味着我们可以使用动态编程,或者简单地“记忆”我们的函数,从而大大提高效率。

    为了进一步加快速度,可以在排列中使用对称性,因为许多排列组是等价的,例如一个排列的反转会产生相同的结果。

    祝你好运。

    【讨论】:

    • 您也可以使用整数的位来表示排列。 15 位。
    【解决方案4】:

    @tiwo ,@rup 考虑到 5 3 2 1 4 是序列首先 alice 删除 5 并且 bob 删除 2 然后序列是 3 1 4 不是递增顺序然后 alice 有机会删除 1 和序列按升序排列 Alice 应该是答案。在您给出的图表中,应该在 3 和 1 之间有一条边,因为 1 和 3 处于反转状态。

    请告诉我我错在哪里,因为问题中给出的答案实际上是 BOB

    【讨论】:

      【解决方案5】:

      你可以用极小极大算法来解决它。这是java中的代码

      import java.io.*;  
      import java.util.*;  
      import java.text.*;  
      import java.math.*;  
      import java.util.regex.*;  
      
      public class Solution {  
      
          public static Scanner sc = new Scanner(System.in);  
          public static void main(String[] args) {  
              int t = ni();  
              for(int i=0; i<t; i++){  
                  int n = ni();  
                  Map<Long, Boolean> map = new HashMap<Long, Boolean>();  
                  int[] numbers = new int[n];  
                  for(int j=0; j<n; j++){  
                      numbers[j] = ni();  
                  }  
                  if(aliceWin(numbers, map)) System.out.println("Alice");  
                  else System.out.println("Bob");  
              }  
          }  
      
          public static boolean aliceWin(int[] a, Map<Long, Boolean> map){  
              long h = hashCode(a); int temp;   
              if(map.containsKey(h)) return true;  
      
              for(int i=0; i<a.length; i++){  
                  if(a[i]>0){  
                      temp = a[i] ;  
                      a[i] = 0;  
                      if(isIncreasing(a)){  
                          map.put(h, true);  
                          a[i] = temp;  
                          return true;  
                      }  
                      if(!aliceWin(a, map)) {  
                          map.put(h, true);  
                          a[i] = temp;  
                          return true;  
                      }  
                      a[i] = temp;  
                  }  
              }  
              return false;  
          }  
      
          public static long hashCode(int[] a){  
              long result = 0;  
              for(int i=0; i<a.length; i++){  
                  result = (result << 4) + a[i];  
              }  
              return result;  
          }  
          public static boolean isIncreasing(int[] a){  
              int last = 0;  
              for(int i=0; i<a.length; i++){  
                  if (a[i] > 0){  
                      if(last > a[i]) return false;  
                      last = a[i];  
                  }  
              }  
              return true;  
          }  
          public static int ni(){  
              return sc.nextInt();  
          }  
      
          public static void print(Object... args){          
              System.out.println(Arrays.deepToString(args));  
          }  
      }  
      

      来自博客:hackerrank-permutation-game

      【讨论】:

      • 当数组不是全局的时,为什么在返回之前更新数组会改变任何东西
      【解决方案6】:

      这里有一些代码可以为您构建图表,但需要您在图表上调用 reverse(),创建一个连接到基础中所有节点的源节点,返回源查看是否有办法让爱丽丝获胜.

      input_ = """2
      
      3
      
      1 3 2
      
      5
      
      5 3 2 1 4""".splitlines()
      
      perms = [map(int,perm.split()) for perm in input_ if len(perm)>1]
      "[['1', '3', '2'], ['5', '3', '2', '1', '4']]"
      
      if networkx is None:
          import networkx
      from itertools import combinations
      
      def build_graph(perm):
          base = set()
          G = networkx.DiGraph()
          for r in range(1,len(perm)+1):
              for combo in combinations(perm,r):
                  combo = list(combo)
                  if combo == sorted(combo):
                      base.add(tuple(combo))
                      continue
                  for i in range(r):
                      G.add_edge(tuple(combo),tuple(combo[:i]+combo[i+1:])) #you may want to reverse the graph later to point from base to source.
          return G,base
      
      
      def solve(G,base):
          #dfs,
          pass
      
      for perm in perms:
          G,base = build_graph(perms[0])
          print solve(G,base)
      

      【讨论】:

        【解决方案7】:

        我们不能只检查每一步... 下一位玩家是否进行了一次更改使序列排序..如果是,则进行其他操作.. 或继续前进

        喜欢 5 3 2 1 4 如果爱丽丝做 3 2 1 4 鲍勃不能通过消除任何...在一个回合中获胜 就像如果 他做了 2 1 4 它没有排序.. 他做了 3 1 4 它没有排序.. 他做了 3 2 4 它没有排序.. 所以 5 3 2 1 4 -> 3 2 1 4 是一个有效的举动!

        现在轮到鲍勃了.. 他会检查同样的.. 但是在某些时候..不会有一个数字可以让您像上面那样采取行动.. 所以你必须做一个随机的动作,然后谁会赢,然后可以很容易地通过将序列变成单个元素的步数来计算!!

        【讨论】:

          【解决方案8】:

          对我来说(几乎用你自己的话):

          如果他/她能够在第一步中严格增加他/她获胜(普通情况),否则获胜者将由该序列中的元素数量决定。

          以你的第二种情况为例。

          我认为图形解决方案很好,但它忘记了玩家以最佳方式进行游戏。所以不需要检查所有不同的路径,因为其中一些会来自非最佳选择。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多