Interviewstreet-排列游戏

时间:2012-04-02 09:53:36

标签: algorithm math game-theory

Alice和Bob玩下面的游戏:

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 <= T <= 100

2 <= N <= 15

最初的排列不会是一个增加的序列。

我正在努力解决上述问题。我已经衍生到很远,但我陷入了困境。请帮我进一步。

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

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

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

因此得出结论:

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

如果他/她能够严格减少,则获胜者由该序列中的元素数决定。如果该序列中有偶数个元素,则当前玩家将失败,否则获胜。

但是,如果结果字符串既不增加也不减少,应该怎么做?

8 个答案:

答案 0 :(得分:9)

这是一个典型的游戏问题。您有2 ^ 15个可能的位置,表示剩余的数字。根据剩余数字的数量,你可以得出轮到它的数量。所以现在你有一个以下面的方式定义的图形 - 顶点是剩余数字的可能集合,并且有一条边连接两个顶点u和v iff有一个移动改变set u to set v(即set v只有一个数字。)

现在你已经指出了哪些位置你知道谁是胜利者 - 那些代表不断增加的数字序列的位置被标记为失败。对于所有其他位置,您可以通过以下方式确定它们是否正在获胜或失去:如果有一个边缘将其连接到松动位置,则获胜。所以剩下的就是像带有记忆的dfs那样你可以确定哪些位置正在赢,哪些位置正在消失。由于图形相对较小(2 ^ 15个顶点),因此该解决方案应该足够快。

希望这有帮助。

答案 1 :(得分:4)

当然,对于小N来说,这可以用“蛮力”来完成,但是你不怀疑inversionsthe sign of a permutation周围的答案是否更容易?

最初我怀疑有一个答案,如果“如果标志是-1,则会失败,否则会失败”,但事实并非如此。

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

反转是一对索引i&lt; j,使得a [i]> a [j]。考虑(ij)具有顶点1,...,N的无向图的边。每个玩家从该图中删除一个顶点,如果没有边缘则获胜。

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

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

并且Alice很快发现删除“5”会让Bob有机会删除2.然后没有留下任何反转,Bob就赢了。

答案 2 :(得分:2)

这个游戏可以递归解决。

每次alice采取她的第一个选择并选择i,从所有剩余的大于i的数字中减去1。现在我们有相同的游戏,但数字1到N-1

假设你的序列是

1,3,5,4,2

在她的第一步中,爱丽丝可以选择任何号码。 情况1: 她选择1,如果鲍勃不能以3,5,4,2(相当于2,4,3,1)获胜,爱丽丝可以获胜

情况2: 她先选了3个。如果鲍勃不能赢得1,5,4,2(相当于1,4,3,2)

,爱丽丝可以获胜

情形3: 她先挑了5个。如果鲍勃不能赢得1,3,4,2

,爱丽丝可以获胜

你明白了。

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

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

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

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

祝你好运。

答案 3 :(得分:1)

@ tiwo,@ rup COnsidering 5 3 2 1 4是序列第一个alice删除5并且bob删除2然后序列是3 1 4,这不是递增顺序然后alice有机会删除1和序列按升序排列,Alice应该是答案。在你给出的图表中,应该有一个介于3和1之间的边,因为1和3是反转的。

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

答案 4 :(得分:1)

您可以使用minimax算法解决它。这是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

答案 5 :(得分:0)

以下是一些为您构建图形的代码,但要求您在图形上调用reverse(),创建连接到基础中所有节点的源节点,返回源代码,看看是否有alice获胜的方式

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)

答案 6 :(得分:-1)

我们不能只检查每一步...... 由下一个玩家进行的单一更改会使序列排序..如果是,则进行其他移动.. 或继续搬家

像 5 3 2 1 4 如果爱丽丝做3 2 1 4 通过消除任何...,鲍勃不能一蹴而就 好像 他做了2 1 4它是排序的.. 他做了3 1 4它是排序的.. 他做了3 2 4它是排序的.. 所以5 3 2 1 4 - &gt; 3 2 1 4是有效的举动!!

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

答案 7 :(得分:-2)

给我(几乎用你自己的话):

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

以你的第二个案例为例。

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