代码改变世界

算法回顾--N皇后问题简单回顾

2014-11-03 23:52  周信达  阅读(584)  评论(0编辑  收藏  举报

前言

最近学习的过程中,不知道哪门子的思维发散,突然又遇见皇后问题了,于是乎老调重弹,心里琢磨,虽然思路大家都容易懂,哪怕是最简单的野蛮回溯法,说着简单,但是如果非得编码实现?我可以一次性写出来OK的代码吗?我对此表示疑问,于是乎动手写代码,发现写此类算法问题,最重要的是边界条件的判断。这里说明一下,这篇纯属练手,不考虑算法效率,只是为了实现,为了练习最暴力野蛮的回溯,说白了,就是怎样简单粗暴的玩弄for和while这些个玩意!

实现

本人比较懒,所以懒得搞二维数组来存储皇后坐标,其实用二维数组来存储,然后根据行列来遍历和匹配,确实效率高了不少,但是,我在这里仅仅只是练习回溯的思想,所以只是使用简单的数组来存储N*N个数,比如8皇后问题,我就用数组来存储 {1,2,3,,,,,,,,,,64},然后使用queens数组存储安全的皇后

代码

/// <summary>
/// 皇后算法非递归实现,主要思想如下:
/// 初始化皇后备选列表为空(安全),将所有坐标的棋子尝试放入
/// 如果符合安全,则放入(如果数目达到则保留输出),如果不安全则跳过继续搜索
/// 如果搜索到结尾依然未搜到,则循环向上回溯到上一次解的下一个继续
/// </summary>
public class Queen
{
    // 矩阵维度
    int size;
    // 从1编号,存放所有整数
    int[] array;
    int count;

    public Queen()
        : this(4)
    {
    }

    public Queen(int s)
    {
        this.size = s;
        InitList();
    }

    private void InitList()
    {
        count = size * size;
        array = new int[count];
        for (int i = 0; i < count; i++)
        {
            array[i] = i + 1;
        }
    }

    public void PrintQueenResult()
    {
        List<int> queens = new List<int>();

        // 记录探测索引
        int tryIndex = 0;
        // 记录最后一次皇后解
        int lastQueen = 0;
        // 记录皇后总数
        int queenCount = 0;
        // 记录所有全皇后解个数
        int solutions = 0;
        while (true)
        {
            // 探测元素
            int tryItem = array[tryIndex];

            // 如果探测到最后一个元素,并且皇后已经回溯为空
            // 表示回溯已经全部完成,返回
            if (tryItem == array[count - 1] && queenCount == 0)
                break;

            // 找到安全的皇后
            if (IsSafe(queens, tryItem))
            {
                queens.Add(tryItem);
                // 皇后数加1
                queenCount++;
                // 记录最后一次放置的皇后
                lastQueen = tryItem;

                // 如果皇后获取足够,输出
                if (queenCount == size)
                {
                    Console.WriteLine("找到皇后排列:{0}",
                        string.Join(",", queens));
                    solutions++;
                }
            }
            else
            {
                if (tryIndex < count - 1)
                {
                    // 如果不安全,还未搜索结束,继续搜索
                    tryIndex++;
                }
                else
                {
                    // 如果不安全,但搜索结束,则回溯
                    while (tryIndex >= count - 1)
                    {
                        queens.Remove(lastQueen);
                        queenCount--;
                        tryIndex = lastQueen;

                        // 如果回溯完毕则跳出
                        if (queenCount == 0)
                            break;

                        lastQueen = queens[queenCount - 1];
                    }
                }
            }
        }

        Console.WriteLine("全部解个数为:{0}", solutions);
    }

    private bool IsSafe(List<int> list, int target)
    {
        if (list == null || list.Count == 0)
            return true;
        return list.All(d => !IsDangerous(d, target));
    }

    private bool IsDangerous(int a, int b)
    {
        int rowA = (a - 1) / size + 1;
        int rowB = (b - 1) / size + 1;
        int colA = (a - 1) % size + 1;
        int colB = (b - 1) % size + 1;

        if (a == b) return true;
        if (rowA == rowB) return true;
        if (colA == colB) return true;
        if (Math.Abs(rowA - rowB) == Math.Abs(colA - colB))
            return true;

        return false;
    }

    private int Pop(List<int> list)
    {
        int last = list[list.Count - 1];
        list.Remove(last);
        return last;
    }

    private int Peek(List<int> list)
    {
        return list[list.Count - 1];
    }
}

测试

八皇后测试代码:Queen q = new Queen(8);  q.PrintQueenResult();

结语

脑子不练会生锈,还是要经常做做这些最基础的东西,把不习惯变成习惯,慢慢来,不早了,先睡了