八皇后问题的解题思路

关于八皇后问题以及回溯递归思想

回溯法---n皇后问题

是回溯递归思想的展现。

回溯法和枚举法的区别

回溯法与穷举法有某些联系,它们都是基于试探的。
穷举法要将一个解的各个部分全部生成后,才检查是否满足条件,若不满足,则直接放弃该完整解,然后再尝试另一个可能的完整解,它并没有沿着一个可能的完整解的各个部分逐步回退生成解的过程。
而对于回溯法,一个解的各个部分是逐步生成的,当发现当前生成的某部分不满足约束条件时,就放弃该步所做的工作,退到上一步进行新的尝试,而不是放弃整个解重来。

实现1:百度百科八皇后问题里面的python实现

def queen(A, cur=0):
    global num
    if cur == len(A):
        print(A)  # 打印解,每个位置对应的列
        num += 1
        return 0
    for col in range(len(A)):
        A[cur], flag = col, True
        for row in range(cur):
            # 如果检测出前面的行和当前行是同列的,或者在当前点的等腰直角三角形上
            if A[row] == col or abs(col - A[row]) == cur - row:
                flag = False
                break
        if flag:  # 如果当前点合适的话那就下一个点,如果当前点不合适的话那就当前点往右再走一步
            queen(A, cur+1)
num = 0
queen([None]*8)
print(num)

暴力解法盲目的枚举算法:通过8重循环模拟搜索空间中的8个状态,从中找出满足约束条件的“答案状态”。下面的算法总共需要8^8=2^24

优点:程序思想比较简单

缺点:问题转化为“从64个格子中选一个子集”,使得“子集中恰好有8个格子,且任意选出两个格子都不在同一行,同一列或者同意对角线上”。这恰好是子集枚举问题。然而,64个格子的子集有2^64个,太大了,则并不是一个很好的模型。

def queens():
    a=[None]*8
    count=0
    for i0 in range(8):
        a[0]=i0
        for i1 in range(8):
            a[1]=i1
            for i2 in range(8):
                a[2]=i2
                for i3 in range(8):
                    a[3]=i3
                    for i4 in range(8):
                        a[4]=i4
                        for i5 in range(8):
                            a[5]=i5
                            for i6 in range(8):
                                a[6]=i6
                                for i7 in range(8):
                                    a[7]=i7
                                    if check(a):
                                        count +=1
                                        # print(' '.join(map(str,a)))
    return count

def check(a):
    n=len(a)
    for i in range(1,n):
        for j in range(i):
            if a[i]==a[j] or abs(a[i]-a[j])==i-j:
                return False
    return True
                    
count=queens()
print(count)

考虑到其中的限制条件,不能同行同列,那么第一行8个位置,第二行7个位置,第三行6个位置,这样算法总共只需要8!=40320

枚举的次数不会超过这个值。当前判断如果走不通就掉头,缺点也是一样的,对于n未知的情况或者n非常大的情况,写法是不好的(需要n个for循环)

def queens():
    a=[None]*8
    count=0
    for i0 in range(8):
        a[0]=i0
        for i1 in range(8):
            a[1]=i1
            if check(a,1):
                continue
            for i2 in range(8):
                a[2]=i2
                if check(a,2):
                    continue
                for i3 in range(8):
                    a[3]=i3
                    if check(a,3):
                        continue
                    for i4 in range(8):
                        a[4]=i4
                        if check(a,4):
                            continue
                        for i5 in range(8):
                            a[5]=i5
                            if check(a,5):
                                continue
                            for i6 in range(8):
                                a[6]=i6
                                if check(a,6):
                                    continue
                                for i7 in range(8):
                                    a[7]=i7
                                    if check(a,7):
                                        continue
                                    else:
                                        count +=1
                                        # print(' '.join(map(str,a)))
    return count

def check(a,n):
    for i in range(n):
        if a[i]==a[n] or abs(a[n]-a[i])==n-i:
                return False
    return True
                    
count=queens()
print(count)

上述,它只针对八皇后问题,解决任意的n皇后问题还要修改程序结构。如果要解决n皇后的问题,就需要将n作为参数传递给函数,函数需要重写来实现回溯(不能采用级联的for循环,n不确定);从另一方面,程序中出现了大量的for循环,而且for中的函数结构很相似,自然想到的是递归迭代回溯。这就是回溯比较常用的两种实现方法:非递归回溯和递归回溯。

递归回溯法:

def queens(A,k=0):
    global count
    if k==len(A):
        print(A)
        count +=1
        return 0
    for i in range(len(A)):
        A[k]=i
        if not check(A,k):
            continue
        else:
            queens(A,k+1)

def check(a,n):
    for i in range(n):
        if a[i]==a[n] or abs(a[n]-a[i])==n-i:
                return False
    return True
                    
count =0
queens([None]*8,k=0)
print(count)

非递归方法:比较复杂,暂时还没有转化成python,下面只给出C++语言的实现

void backdate (int n)
{  
    int count = 0;
    int a[100];

    int k = 1;
    a[1]=0;   
    while(k>0)
    {
        a[k]=a[k]+1;//对应for循环的1~n
        while((a[k]<=n)&&(!check_2(a,k)))//搜索第k个皇后位置
        {
            a[k]=a[k]+1;
        }
          
        if(a[k]<=n)//找到了合理的位置
        {
            if(k==n )
            {//找到一组解
                for(int i=1;i<=8;i++)  
                {
                    cout<<a[i];
                }
                cout<<endl;
                count++;
            } 
            else 
            {
                k=k+1;//继续为第k+1个皇后找到位置,对应下一级for循环 
                a[k]=0;//下一个皇后一定要从头开始搜索
            }
        }
        else
        {
            k=k-1;//回溯,对应执行外内层for循环回到更上层 
        }
    }
    cout<<count<<endl;
}

void main()
{
    backdate(8);
}

下面的一道题

描述

会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。
给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。

输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1 <= b <= 92)

输出

输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。

样例输入

2
1
92

样例输出

15863724
84136275
# 给n皇后问题的所有解进行排序,按从小到大的顺序:例如,其中三组解[6 4 2 0 5 7 1 3],[6 1 5 2 0 3 7 4],[7 2 0 5 1 4 6 3];
# 排序后是[6 1 5 2 0 3 7 4],[6 4 2 0 5 7 1 3],[7 2 0 5 1 4 6 3]
# 从上面打印可以看出,打印的顺序就是从小打到的顺序。
def queens(A,k=0):
    global count,seek
    if k==len(A):
        # print(A)
        count +=1
        if count==seek:
            print(A)
        return 0
    for i in range(len(A)):
        A[k]=i
        if check(A,k):
            queens(A,k+1)

           
def check(a,n):
    for i in range(n):
        if a[i]==a[n] or abs(a[n]-a[i])==n-i:
                return False
    return True
                    
count,seek =0,1
queens([None]*8,0)
print(count)

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值