【Python】蓝桥杯:散

 1.马走日

题目如图

        整体思路于dfs大致,利用递归和遍历,由于日字走法的特殊性,这次需要遍历八个方向,以一个坐标图表示

        从(0,0)开始,在第一象限可以走到(1,2)和(2,1),同理在其他象限也一样,这样我们就得出八个方向,将八个方向储存至列表中dirs=[(2,1),(1,2),(-2,1),(-1,2),(2,-1),(1,-2),(-1,-2),(-2,-1)]。建立一个全为0的二维数组用于表示棋盘,然后我们定义一个函数,用step来记录走过的步数,当step=n*m时就表示已经遍历过一遍棋盘,再用cut在统计遍历的次数,再来进行八个方向的写法,遍历列表,将列表中第i个元组的第1个元素赋值给x,将列表中第i个元组的第2个元素赋值给y,当x与y没有过界时,且当前位置为0时,就把当前位置改为1,重新递归后step+1且重置棋盘为0。

s=int(input())
for _ in range(s):
    m,n,a,b=map(int,input().split())
    dirs=[(2,1),(1,2),(-2,1),(-1,2),(2,-1),(1,-2),(-1,-2),(-2,-1)]
    borad=[[0]*n for x in range (m)]
    cut=0
    def move(x,y,step):
        global cut
        if step==n*m:
            cut+=1
            return
        for i in range(8):
            move_x=x+dirs[i][0]#表示列表中第i个元组的第一个元素
            move_y=y+dirs[i][1]
            if move_x>=0 and move_x<=m-1 and move_y>=0 and move_y<=n-1 and borad[move_x][move_y]==0:
                borad[move_x][move_y]=1
                move(move_x,move_y,step+1)
                borad[move_x][move_y]=0


    borad[a][b]=1
    move(a,b,1)
    print(cut)

2.N皇后问题

题目如图

        该题跟上题解法类似,用到了遍历,递归,回溯。根据题意棋子不能在同一行,同一列,同一对角线, 难点在于对于对角线的设计。这里定义了两种对角线为正对角线和副对角线,我们通过计算可以算出对角线的数量是等于2*n-1的,那么我们就可以创建2*n-1条主对角线和副对角线,再创建n列和n行,其中将创建的对角线和列和行赋值为0。

创建函数,对主对角线做如下处理

        由于主对角线是从棋盘的左上角到右下角的对角线,可以看出主对角线位置=x-y,但因为数组索引是从 0 开始的,所以我们应进行偏移改为x-y+n-1这样就保证了是从0开始索引。同理副对角线是从左下角到右下角,则可以直接用x+y来表示。

        然后就可以设立一个判断,如果主队对角线=0,副对角线等于0,且该列也等于0,就可说明该点未被标记,然后我们将各均军标记为1,说明该位置的对角线与列不能放棋子并标记当前行 row 上已经放置了一个元素。接着判断该行是否小于n-1,如果小于则进行递归且(row+1),否则已经遍历完一遍,利用计数器来统计次数,并重置所有数值为0。

若想将所有结果打印可以使用如下代码

def printsolu():
    for i in range(n):
        for j in range(n):
            if rows[i]==j:
                print("A",end=" ")
            else:
                print("0",end=" ")
        print()
    print()

将棋子放置的位置设为A。

完整代码如下

n = int(input())
maindiag=[0 for x in range(2*n-1)]#主对角线
subding=[0 for x in range(2*n-1)]#副对角线
lie = [0 for x in range(n)]#列
rows=[0 for x in range(n)]
cut=0
def printsolu():
    for i in range(n):
        for j in range(n):
            if rows[i]==j:
                print("A",end=" ")
            else:
                print("0",end=" ")
        print()
    print()
def search(row):
    global cut
    for col in range(n):
        x = row ; y = col
        if maindiag[x-y+n-1]==0 and subding[x+y]==0 and lie[y]==0:
            rows[row] = col#标记当前行 row 上已经放置了一个元素
            maindiag[x-y+n-1] = 1
            subding[x+y] = 1
            lie[col] = 1
            if row < n-1:
                search(row+1)
            else:
                cut+=1
                printsolu()
            maindiag[x-y+n-1] = 0
            subding[x+y] = 0
            lie[col] = 0
            rows[row] = 0
search(0)
print(cut)

3.数独游戏

题目如图

        这题采用暴力遍历和递归来解决(并非最优解),首先我们定义一个函数用于判断某数是否已经出现在同一行和同一列以及在一个区块中(如果数独是4*4,那一个区块就是2*2)。行判断用if num in a[x]: 列可以使用if num in list(zip(*a))[y]:,其中zip 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象。*a 是一个解包操作,它将二维列表 a 的行作为独立的参数传递给 zip 函数。这样,zip(*a) 的结果就是按列将 a 中的元素组合起来。例如:

a = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]

那么 zip(*a) 的结果会是:

((1, 4, 7), (2, 5, 8), (3, 6, 9))

然后判断在一个区块是否有重复 。

        接下来定义一个find()函数用于用于填充 a 中的缺失数字。它遍历 a 中的每个位置,并尝试放置 1 到 4 的数字。如果数字有效(通过 valid 函数检查),则将其放置在 a 中,并递归调用 find 以尝试填充下一个位置。如果返回(意味着它无法找到有效的填充方式),则将当前位置的数字重置为 0,并尝试下一个数字。

完整代码如下

def valid(x,y,num):
    if num in a[x]:
        return False
    if num in list(zip(*a))[y]:
        return False

    ax,ay = x//2,y//2
    for x in range(2):
        for y in range(2):
            nx,ny = ax*2 + x, ay*2 + y
            if num == a[nx][ny]:
                return False
    return True

a = [
    list("1234"),
    list("4321"),
    list("3100"),
    list("0000")
]
a=[[int(x) for x in row] for row in a]

def find():
    for i in range(4):
        for j in range(4):
            if a[i][j]==0:
                for num in (1,2,3,4):
                    if valid(i,j,num):
                        a[i][j]=num
                        find()
                        a[i][j]=0
                return

    for row in a:
        print(row)

find()

  • 53
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值