问题描述
要求找出一个nxn棋盘上放置n个皇后并使其不能相互攻击的所有方案。即任意两个皇后不在同一行或同一列,且不在同一斜角线上。
解决思想
利用回溯的方法来解决,对于回溯问题,一个好的限界函数会起到很好的效果,否则,可能效果很差。
代码实现
import matplotlib.pyplot as plt
import copy
# 限界函数,来判断是否可放置一个皇后
def place(X,k):
# 如果一个皇后能放在第k行和X(k)列,则返回true;否则返回false。
# X是一个全程数组(在python中换成列表),进入此过程时已置了k个值。
# abs(r)过程返回r的绝对值
i=0
while(i<k):
if X[i]==X[k] or abs(X[i]-X[k])==abs(i-k):
return False
i+=1
return True
# n-皇后问题的所有解
def nqueens(n):
# 此过程使用回溯法求出一个nxn棋盘上放置n个皇后,使其不能相互攻击的所有可能位置
# global X
allX=[] #该变量用于存放所有的可行解
X=[-1] #给X赋初值,即X[0]=-1
k=0 #k为当前行,给它赋0只是下标对应的0,实际指第一行
while(k>-1):
X[k]+=1 #移到下一列
while(X[k]<=n-1 and not place(X,k)): #判断此处能否放置该皇后
X[k]+=1
if X[k]<=n-1: #找到一个位置
if k==n-1: #判断是否是完整的解
allX.append(copy.deepcopy(X)) #需要进行深拷贝,否者值会被删除(因为是引用)
# print(X)
# print(allX)
else:
k+=1
X.append(-1) #相当于给X[k]赋值-1,即转到下一行
else:
X.pop(-1)
k-=1
return allX
# 绘图函数,将各个解对应的情况绘制出来
def plot(allX2):
length=len(allX2) #解的数量
name=1 #图片名字,以序号命名
for i,X in enumerate(allX2):
tick=list(range(len(X))) #坐标轴上的刻度值
x_data=[] #点的横坐标
y_data=[] #点的纵坐标
for j,value in enumerate(X):
x_data.append(value+0.5)
y_data.append(j+0.5)
if(i%16==0):
plt.figure(i//16+1) #生成一个新图
plt.subplot(4,4,i%16+1) #每张图最多画16个子图
plt.scatter(x_data,y_data)
plt.title(str(i+1))
plt.xticks(tick)
plt.yticks(tick)
plt.grid(True)
if(i%16==15 or i==length-1):
# print(i,length-1)
plt.savefig(str(name)+'.jpg') #保存图片
name+=1
plt.show()
if __name__=='__main__':
plot(nqueens(8)) #实现8皇后问题
实现效果
由于不知道咋在一行显示多张图片,只能这样看了(卑微)