蓝桥杯四阶幻方(递归-一维列表下标在二维平面上编号)

1. 问题描述:

把1~16的数字填入4x4的方格中,使得行、列以及两个对角线的和都相等,满足这样的特征时称为:四阶幻方。 四阶幻方可能有很多方案。如果固定左上角为1,请计算一共有多少种方案。 
比如: 

1  2 15 16 
12 14  3  5 
13  7 10  4 
8 11  6  9 

以及: 

1 12 13  8 
2 14  7 11 
15  3 10  6 
16  5  4  9 

就可以算为两种不同的方案。

输出

请提交左上角固定为1时的所有方案数字 

来源:http://oj.ecustacm.cn/problem.php?id=1265

2. 思路分析:

① 分析题目可以知道这是一道需要求解全部方案的题目,所以需要使用递归来搜索所有的可能性。一开始想到的是逐行生成二维列表中的数字,使用for循环尝试填进当前未被使用过的数字,然后往下递归,当递归到每一行末尾的时候(第四列的时候)那么判断当前这一行的和是否满足34(因为题目中要求每一行、每一列、对角线都是相等而1-16的和除以4那么就可以得到每一行、列、对角线的和都为34),当递归到第四行的时候依次判断对应当前列数的对应的和是否满足34如果不满足那么就需要return到上一层,并且在每一行三个数字填好之后判断当前填入的数字是否加上16都是小于34的如果小于34那么说明当前循环中的数字都是不满足题目要求的直接return上一层(这些判断都是剪枝),但是使用python语言还是运行了很久没有计算出结果,可能还是存在很多不满足条件继续递归的情况。

② 后面与同学的讨论中发现了一种比较好的方法,思路是使用一维列表来记录当前已经生成的数字,并且将一维列表的下标在二维列表中编好填入数字的顺序(其实就是将当前一维列表的数字根据固定的位置填入到二维坐标中),这样就可以在递归方法一开始的时候进行判断是否满足条件,也即可以根据一维列表中生成的元素个数对应的行、列、对角线的和提前判断是否可以往下递归,编号的顺序为如下图所示,下面这个图是规定好的顺序(可以为其他的顺序关键是要求限制的条件尽可能多这样会过滤掉很多不满足条件往下递归的例子),相当于我是将生成好的数字按照图中的顺序填进去(其实与逐行生成数字没有什么两样只是这里规定好了顺序了)。比如列表长度为4的时候那么可以判断第一行的和是否满足34,当列表长度为7的时候判断判断第一列的和是否满足34,为列表长度为10的时候判断第二列的和是否满足34,为列表长度为11的时候判断3/10/8/6对角线的和是否满足34,为12的时候判断第二行的和是否满足34....当发现不满足条件的时候直接return到上一层这样会很快计算出结果。这个思路确实是个不错的思路,按照元素个数越少但是可以判断的条件越多的一维列表下标的顺序填进二维列表中可以避免大量不满足条件的递归,这个比在二维列表中逐行生成元素判断的效率是高很多的,之后遇到类似的二维坐标满足某行、列、对角线之间的关系的搜索问题都是可以借助于这种方式解决的。

3. 代码如下:

一开始依次生成二维列表中每一行数字的代码(运行了很久没有计算出最终的结果,还是存在很多不符合条件往下递归的搜索)

from typing import List

res = 0
count = 0

def dfs(x: int, y: int, matrix: List[List[int]], nums: List[int], vis: List[int]):
    global count
    count += 1
    global res
    if x == 3 and y == 4:
        # 验证对角线
        if sum(matrix[i][i] for i in range(4)) != 34: return
        if sum(matrix[i][3 - i] for i in range(4)) != 34: return
        res += 1
        print(matrix)
        return
    # 因为每一次递归的时候都是列数加1的当发现列数为4的时候那么就要换下一行了
    if y == 4:
        x += 1
        y = 0
    for i in range(len(nums)):
        if vis[nums[i]] == 0:
            # 减枝
            matrix[x][y] = nums[i]
            # 验证行
            if y == 3:
                # 前面三个数字相加加上16都小于34说明后面的数字都需要尝试了需要退回到上一层尝试其他的数字
                if sum(matrix[x][i] for i in range(3)) + 16 < 34:
                    matrix[x][y] = 0
                    return
                # 当前这一行数字和不等于34那么需要尝试循环中的其他数字
                elif sum(matrix[x][i] for i in range(4)) != 34:
                    matrix[x][y] = 0
                    continue
                    # 不能够return而是continue尝试其他的可能的方案
                    # return
            # 验证列
            if x == 3:
                # 当第三行的时候验证每一列是否满足要求
                if sum(matrix[i][y] for i in range(3)) + 16 < 34: return
                elif sum(matrix[i][y] for i in range(4)) != 34:
                    matrix[x][y] = 0
                    continue
                    # return
            vis[nums[i]] = 1
            dfs(x, y + 1, matrix, nums, vis)
            matrix[x][y] = 0
            vis[nums[i]] = 0


if __name__ == '__main__':
    nums = [i for i in range(1, 17)]
    vis = [0] * 17
    matrix = [[0] * 4 for i in range(4)]
    dfs(0, 0, matrix, nums, vis)
    print(res)

一维列表的下标顺序在二维列表中编号:

from typing import List
res = 0
def dfs(count: int, rec: List[int], vis: List[int]):
    global res
    # 根据一维列表在二维列表的位置判断是否满足条件
    if count >= 4 and rec[0] + rec[1] + rec[2] + rec[3] != 34:
        return
    elif count >= 7 and rec[0] + rec[4] + rec[5] + rec[6] != 34:
        return
    elif count >= 10 and rec[1] + rec[7] + rec[8] + rec[9] != 34:
        return
    elif count >= 11 and rec[3] + rec[6] + rec[8] + rec[10] != 34:
        return
    elif count >= 12 and rec[4] + rec[7] + rec[10] + rec[11] != 34:
        return
    elif count >= 14 and rec[5] + rec[8] + rec[12] + rec[13] != 34:
        return
    elif count >= 15 and rec[2] + rec[10] + rec[12] + rec[14] != 34:
        return
    elif count >= 16 and (rec[6] + rec[9] + rec[14] + rec[15] != 34 or rec[3] + rec[11] + rec[13] + rec[15] != 34 or rec[0] + rec[7] + rec[12] + rec[15] != 34):
        return
    if count == 16: res += 1
    for i in range(2, 17):
        # 未使用到当前的数字
        if vis[i] == 0:
            vis[i] = 1
            rec.append(i)
            dfs(count + 1, rec, vis)
            # 回溯
            vis[i] = 0
            rec.pop()


if __name__ == '__main__':
    # 使用一维列表进行优化, 一维列表在递归的时候可以将坐标映射到二维列表进行剪枝
    import time
    # 计算起始时间
    start = time.time()
    rec = [1]
    vis = [0] * 17
    vis[1] = 1
    dfs(1, rec, vis)
    print(res)
    print(time.time() - start)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于给定的二维数组nums,我们可以对每一个元素num[i]进行一系列操作。首先,我们需要了解二维数组的结构,它由多行和多列组成。每一行都可以看作是一个一维数组,而每一列都可以看作是一个数列。在处理二维数组的时候,我们通常会用到嵌套循环来遍历它的每一个元素。 对于每一个元素num[i],我们可以进行多种操作。其中最常见的操作是查找和修改。在查找元素时,我们一般采用线性查找或二分查找的方式。线性查找适用于无序数组,时间复杂度是O(n);而二分查找适用于有序数组,时间复杂度是O(logn)。因此,在查找元素时,需要根据实际情况选择不同的算法。 在修改元素时,我们可以直接对元素进行赋值操作,或者通过指针修改元素的值。对于二维数组中的元素,我们可以使用二重循环来遍历每一个元素,并使用下标来访问它们。例如,nums[i][j]表示第i行第j列的元素。对于修改元素,我们只需要通过下标对元素进行赋值操作即可。 除了查找和修改操作之外,我们还可以对二维数组进行排序、求最大值、最小值、平均值等操作。这些操作通常需要使用一些常用的算法,如冒泡排序、快速排序、选择排序、插入排序等。此外,还可以使用递归算法对二维数组进行遍历。 总的来说,对于二维数组nums中的每一个元素num[i],都可以进行多种操作,具体操作根据实际需求进行选择。我们可以利用二重循环和下标来访问、修改、查找元素,使用常用算法来求解一些问题。对于需要递归遍历的情况,可以考虑使用递归算法来实现。同时,也需要注意对数组的边界进行判断,防止出现数组越界情况。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值