八皇后问题

两个版本。思路大体相同,都是用的回溯。不过数据结构上的版本不太容易懂。先写上来吧。

struct Queen {
    int x, y;
    Queen(int xx, int yy) :x(xx), y(yy) {};
    bool operator==(Queen const& q)const
    {
        return (q.x == x) || (q.y == y) || (x + y == q.x + q.y) || (x - y == q.x - q.y);//在判断这里巧妙的解决了对角线的问题
    }
    bool operator != (Queen const& q)const
    {
        return !((*this) == q);
    }
};
static int nCheck = 0;
static int nResult = 0;
void placeQueens(int N)
{
    stack<Queen> solu;
    Queen q(0, 0);
    do {
        if ((solu.size() >= N) || (q.y >= N))//y值大于N越界不必多说,此外若栈中储存元素达到n
                                             //个,说明此时已经是一组解。所以相当于越界了,回溯
        {
            q = solu.pop();//这里就是回溯的具体解决方案,将上一个储存元素弹出
            q.y++;//并使列增加,这里不管增加后有没有越界,由下一次循//环来判断   
        }
        else//这里用if,else,因为不能保证回溯后是否能继续执 //行,干脆用分支,用下一次的if判断来保证
        {
            while ((q.y < N) && (solu.find(q) >= 0)//搜寻可放置皇后的位置并记录步数
            {
                q.y++;
                nCheck++;
            }
            if (q.y <N)//如果找到一个可以放置的地方,就将其入栈,判断能否 //输出完整的解,并搜寻下一行,从第0列开始
            {
                solu.push(q);
                if (solu.size() >= N)
                    nResult++;
                q.x++;
                q.y = 0;
            }
        }while ((q.x >0) || (q.y < N))//退出循环的条件是搜寻到第0行第N列
    }

一些总结:
①退出循环的条件是搜寻到第0行第N列
②进入程序后会发生以下几种情况:
(一)满足循环条件,没有越界,进过循环判断后可以放置,并且不是最后一行,那么下一步就是入栈并执行下一行
(二)满足循环条件,没有越界,进过循环判断后可以放置,是最后一行,下一步仍然是执行下一行,但是会回溯
(三)满足循环条件,没有越界,进过循环判断后没有可以放置的位置,那么经过循环之后q.y应该是等于N的,跳过入栈,到下次循环会回溯
(四)solu.size() >= N,即栈满了,此时像越界一样处理,回溯并y++
(五)q.y >=N,越界,需要回溯
③相比于递归来说比较难理解,但是空间复杂度较少,而且通过剪枝也得到了很好的优化。

///
八皇后递归版

const int Normalize = 9;

int Num;
int q[9];

bool s[9];
bool L[17]; //用来存放对角线标记的
bool Y[17];
void try(int col)
{
    if (col == 9)
    {
        Num++;//此时q中存放一个解,可输出
    }
    for (int row = 1; row <= 8; row++)
    {
        if (S[row] && R[col + row] && L[col - row + Normalize])
        {
            q[col] = row;//标记呃,递归,回溯
            S[row] = false;
            R[col + row] = false;
            L[col - row + Normalize] = false;
            Try(col + 1);
            S[row] = true;
            R[col + row] = true;
            L[col - row + Normalize] = true;
        }
    }
}
int main()
{
    Num = 0;
    for (int i = 0; i < 9; i++)
        S[i] = true;
    for (int i = 0; i < 9; i++)
    {
        L[I] = R[i] = true;
    }
    try(1);
    return 0;
}

感觉递归版没啥好说的,简单便捷,回溯也方便》。。。


11.26新增
这次是lisp版的,太简洁了,lisp果然是神书…

(define (queens board-size)
    (define (queen-cols k)
        (if (=k 0)
            (list empty-board)
            (filter
                (lambda (positions) (safe? k positions))
                (flatmap (lambda (rest-of-queens) (map (lambda (new-row) (adjoin-position new-row k rest-of-queens)) (enumerate-interval 1 board-size))) (queen-cols (-k 1))))))
    (queen-cols board-size))
(define empty-board '())
;定义空棋盘
(define (adjoin-position new-row k rest-of-queens)
    (cons new-row rest-of-queens))
;添加皇后

删除不安全的皇后

(define (safe? k position)
    (iter-check (car position) 
                (cdr position)
                 1))

(define (iter-check row-of-new-queen rest-of-queens i)
    (if (null? rest-of-queens)  ; 下方所有皇后检查完毕,新皇后安全
        #t
        (let ((row-of-current-queen (car rest-of-queens)))
            (if (or (= row-of-new-queen row-of-current-queen) ; 行碰撞 (= row-of-new-queen (+ i row-of-current-queen)) ; 右下方碰撞 (= row-of-new-queen (- row-of-current-queen i)))    ; 左下方碰撞
                #f
                (iter-check row-of-new-queen (cdr rest-of-queens) ; 继续检查剩余的皇后 (+ i 1))))))            ; 更新步进值

解释:rest-of-queens是在前k-1列防止k-1个皇后的一种方式,new-row是在第kie放置所考虑的行编号。adjoin-position是将一个新的行列格局加入到一个格局集合;empty-board是一个空的格局集合,safe?是确定新的格局中的kie的皇后是否是安全的。
让我们从表达方式开始说明。

这是书上的例子
表达出来就是list(6 3 1 7 5 8 2 4)
因为题目要求给出八皇后问题的所有解法,所以 queens 求出的最终结果将是一个二维列表: (list (list 6 3 1 7 5 8 2 4) (list …) (list …) …) 。

(define (safe? k position)
    (iter-check (car position) 
                (cdr position)
                 1))

(define (iter-check row-of-new-queen rest-of-queens i)
    (if (null? rest-of-queens)  ; 下方所有皇后检查完毕,新皇后安全
        #t
        (let ((row-of-current-queen (car rest-of-queens)))
            (if (or (= row-of-new-queen row-of-current-queen) ; 行碰撞 (= row-of-new-queen (+ i row-of-current-queen)) ; 右下方碰撞 (= row-of-new-queen (- row-of-current-queen i)))    ; 左下方碰撞
                #f
                (iter-check row-of-new-queen (cdr rest-of-queens) ; 继续检查剩余的皇后 (+ i 1))))))            ; 更新步进值

safe 这个函数,调用了一个iter-check函数,iter-check函数传入的参数有三个(其实是两个),新加入的行,还有之前的行,在这里之前的行我们已经可以保证它是符合规定的。
然后就是遍历检查了,之前存的时候从上向下存的一个好处在这就能体现了,从上向下便利检查每一个数字,并且也要看对角线是否满足要求,如果到最后都检查完了就是成功了,否则就是失败。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值