n皇后问题 [随机化算法,拉斯维加斯算法]

问题:

  如何能够在 n×n 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

分析:

  这题常规的解法应该是回溯法,然而回溯法的话,要遍历所有的情况。

  这里介绍一种随机化的算法:

  我们先摆开头的几个棋子,然后剩下的棋子用回溯法来做,由于解空间树的头几层不用拿来遍历了,回溯的时候遍历的结点少了很多。

  研究标明,随机摆开头的一半略少的棋子,可以很快得得到解。当然,这个算法是只能求出一部分的解的,但是在 n 很大的时候速度比回溯法快了非常多。

  ps:回溯法是可以得到所有解的。

做法:

  先随机摆头几个棋子,这里有一个很棒的算法,参考:http://blog.csdn.net/yusiguyuan/article/details/42607681

  但是为了方便,我这里的做法是对一个数组中的所有元素做几次随机的交换。  

void randomPlace(int n, int pieces[])
{//随机摆放棋子
    srand((unsigned)time(NULL));
    for (int i = 0; i < n; i++)
    {
        int a = random(n) + 1;
        int b = random(n) + 1;
        swap(pieces[a], pieces[b]);
    }
}

  然后对接下来的棋子用回溯法:  

void nQueen(int n, int t, int pieces[])
{//回溯法解n后问题
    if (t > n)
    {
        resultNumber++;//计算解的个数
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j < pieces[i]; j++)
                cout << "- ";
            cout << pieces[i] << " ";
            for (int j = pieces[i] + 1; j <= n; j++)
                cout << "- ";
            cout << endl;
        }
        cout << endl;
    }
    else
    {
        for (int i = t; i <= n; i++)
        {
            swap(pieces[t], pieces[i]);
            if (isOK(t, pieces))
            {
                nQueen(n, t + 1, pieces);
            }
            swap(pieces[t], pieces[i]);
        }
    }
}

  这两个函数应该在LasVegas函数中调用,知道得出至少一个解:  

void LasVegas(int n, int pieces[])
{//拉斯维加斯算法:
    //先随机摆前面的一些棋子,然后后面的用回溯法来解
    if (n == 1)
    {//特殊情况
        cout << 1 << endl;
    }
    else
    {
        for (int i = 1; i <= n; i++)
        {
            pieces[i] = i;
        }
        while (resultNumber == 0)
        {
            //前几个摆的也要合理才行
            while (!isOK(n / 2, pieces))
            {
                randomPlace(n, pieces);
            }
            nQueen(n, (n / 2)-1, pieces);//随机一半略少的棋子摆放
        }
    }

}

  代码是从 n皇后问题改过来的,其中部分代码我这里就不显示出来了。

 

posted @ 2016-11-27 10:11  郑龙天  阅读(3928)  评论(0编辑  收藏  举报