3

我们遇到了一个问题,我将其简化为以下内容:给您一个全为二进制数(例如 11111)和一组相同长度的二进制数(00101、10000、01100、00100、11100)。有两个玩家 A 和 B。在每一轮,玩家可以从主二进制数 (11111) 中减去任何一个较小的数字,这样 2 的二进制与就是较小的数字。然后下一个玩家可以从结果中减去,依此类推。不能再减去的玩家就输了。例如。

   A         B       
 11111     11010   // Now A cannot subtract 
-00101    -10000   // anymore in the next turn. 
-------   ------   // So B wins the game.
 11010     01010
-------   ------

如果两个玩家都发挥最佳(为他们的胜利做出最佳选择),我必须找出给定二进制数字组合的哪个玩家获胜。

我尝试过 O(n^2) 方法,但有更快的方法吗?

编辑: O(n^2) :其中 n 是状态数。对于长度为 6 (111111) 的二进制数,可能有 2^6 种状态。所以我的复杂度是 O((2^6)^2)。

编辑:生成所有可能状态的我的代码:

void makeAllStates() /* Bottom Up Approach. Starting from 00000 and going to 11111 */
{
    // bool states[i] : True if state[i] is a winning position.
    // bool isWord[i] : True if the given state matches a smaller number. (eg. If the main number has been reduced to 10110 and there is a smaller number 10110, then isWord[i] is true.
    // bool visited[i] : True If the given state has been visited  
    // int statecount : Total number of states
    int temp;
    for(int xx=1;xx<stateCount;xx++)
    {
        for(int yy=1;yy<stateCount;yy++)
        {
            if(xx&yy)
                continue;
            if(!(isWord[xx] || isWord[yy]))
                continue;
            if(!visited[yy])
                continue;
            temp = xx^yy;
            if(isWord[temp])
                continue;
            if(states[temp])
                continue;
            if(isWord[xx] && isWord[yy])
                states[temp] = false;
            else
            {
                if(isWord[xx])
                    states[temp] = !states[yy];
                else
                    states[temp] = !states[xx];
            }
            visited[temp] = true;
            if(temp == stateCount-1 && states[temp])
            {
                return;
            }

        }
    }
}
4

2 回答 2

3

我不知道它是否会帮助你(你告诉过 O(n^2) 方法,但没有说 N 是什么意思)。尝试公平游戏的通用方法(Sprague-Grundy 理论):

  • 游戏中的位置是你的主号
  • 找到所有“亏损”的头寸(你不能再减去任何东西的头寸)
  • 对于所有“失去”的位置 x :Grundy 函数 g(x) = 0;
  • 然后,如果你想计算位置 y 的 grundy 函数:找到所有位置 x_1...x_k,这样你就可以从位置 y 转向位置 x_i。g(y) = mex(g(x_1),...,g(x_k))。“mex”是“最小排除项”——除 g(x_1),...,g(x_k) 之外的所有最小非负整数。例如,mex(2, 3, 4) = 0,mex(0, 1, 2, 5) = 3,mex(0, 1) = 2,等等。

请注意,您可以递归地考虑每个游戏位置,并且您将考虑位置 x 一次(在计算 g(x) 时),因此该算法与可能位置的数量成线性关系。与位置之间可能的转数成线性关系,即 O(N*K) 其中 N 是状态数,K 是较小数字集的大小(您可以在游戏中转数)

如果 g(START_POSITION) = 0,则起始位置为输局,第一个玩家输掉(每回合都会导致一个获胜位置)。如果 g(START_POSITION) > 0,则起始位置为获胜位置(存在转向位置 x 使得 g(x) = 0),因此第一个玩家获胜。

抱歉英语不好,希望对您有所帮助

于 2013-02-08T20:43:38.443 回答
0

根据 K.Bulatov 的输入,这是最坏情况下时间复杂度为 O(n^2) 的最终代码。剪枝后,调用次数大大减少。主要功能如下:

//state : The state for which grundy number is being queried.
//visited[i] : If the grundy number of the state has already been calculated.
//wordStates[] : an array with all the smaller numbers stored.
//mex() : "minimal excludant" - the smallest non-negative integer not in array
int grundyNum(int state)
{
    if(visited[state])
        return grundy[state];
    int grundArr[wordStates.size()];
    int loc =0;
    visited[state] = true;
    for(int xx =0;xx<wordStates.size();xx++)
    {
        if((state&wordStates[xx]) == wordStates[xx])
        {
            grundArr[loc] = grundyNum(state^wordStates[xx]);
            loc++;
        }
    }
    grundy[state] =  mex(grundArr,loc);
    return grundy[state];
}

只需使用以下内容调用该函数即可获得获胜者:

result = grundy(1111111);
winner = result==0?"B":"A";
于 2013-02-09T10:03:48.110 回答