Python随便刷刷

1.九宫幻方

题目描述
小明最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分,三阶幻方指的是将 1~9 不重复的填入一个 3*3 的矩阵当中,使得每一行、每一列和每一条对角线的和都是相同的。
三阶幻方又被称作九宫格,在小学奥数里有一句非常有名的口诀:“二四为肩,六八为足,左三右七,戴九履一,五居其中”,通过这样的一句口诀就能够非常完美的构造出一个九宫格来。
4 9 2
3 5 7
8 1 6
有意思的是,所有的三阶幻方,都可以通过这样一个九宫格进行若干镜像和旋转操作之后得到。现在小明准备将一个三阶幻方(不一定是上图中的那个)中的一些数抹掉,交给邻居家的小朋友来进行还原,并且希望她能够判断出究竟是不是只有一个解。而你呢,也被小明交付了同样的任务,但是不同的是,你需要写一个程序。

输入描述
输入仅包含单组测试数据。
每组测试数据为一个 3*3 的矩阵,其中为 0 的部分表示被小明抹去的部分。
给出的矩阵至少能还原出一组可行的三阶幻方。

输出描述
如果仅能还原出一组可行的三阶幻方,则将其输出,否则输出"Too Many"(不包含引号)。

import os
import sys


# 请在此输入您的代码
'''
经典排列组合题,虽然题目是二维,其实可以压缩为一维来做。
因为学递归嘛,所以这里使用递归的方法(枚举法也是可以的,我们先找出所有可行解再字符匹配就行了)
本质上,这题可以看成是1-9这九个数的全排列,得到每一个排列我们逐一验证是不是幻方就行
但是直接九个数全排列可能会超时(实际上也不会,大概13个数的全排列才会超时)
那么题目其实只要我们填空格的位置数目,其他位置都是固定的,因此我们只需对空格未出现的数全排列即可
下面用num记录哪些数是没出现的,然后对这些数进行全排列
'''
# matrix用来获取输入的幻方
matrix = ''
num = '123456789'
for i in range(3):
  datain = input().replace(' ', '')
  matrix += datain

# 剔除出现过的数字
for i in matrix:
  if i in num:
    num = num.replace(i, '')

# 这个Huanfang用来存储所有可行解
huanfang = []

# 这个函数用来验证是不是幻方
def check(s, m):
  # 先转换下数据格式,就是要将s填进0的位置,这样就组合成最后填满的九宫格
  idx = 0
  s0 = [x for x in s]
  m0 = [x for x in m]
  o = ''
  for i in range(len(m)):
    if m0[i] == '0':
      o += s0[idx]
      idx += 1
    else:
      o += m0[i]
  # 因为o存储的是字符串,我们要把它转化为数字,这样才能比较大小
  fang = [int(x) for x in o]
  # 判断是否是九宫幻方
  flag = True
  for i in range(3):
    if sum(fang[i*3:i*3+3]) != 15:
      flag = False
      break
    if (fang[i]+fang[i+3]+fang[i+6]) != 15:
      flag = False
      break
  if (fang[0]+fang[4]+fang[8]) != 15 or (fang[2]+fang[4]+fang[6]) != 15:
    flag = False
  # 如果是,则保存此幻方
  if flag:
    huanfang.append(o)


# 这里就是全排列的递归算法
def dfs(cur, s, vis):
  # 终止条件是排完了,即当前位置已经排到最后一个数
  if cur >= len(num):
    # 判断是不是幻方
    check(s, matrix)
    # 这里也算是个小技巧了,如果发现有两个解,直接终止程序,没必要再跑了
    if len(huanfang)>1:
        print('Too Many')
        sys.exit(0)
    return
  for i in range(len(num)):
    if vis[i]>0:
      vis[i] -= 1
      dfs(cur+1, s+num[i], vis)
      vis[i] += 1

dfs(0, '', [1]*len(num))

out = huanfang[0]
for i in range(3):
    print(out[i*3],out[i*3+1],out[i*3+2])

2.剪格子

题目描述
如下图所示,3 x 3 的格子中填写了一些整数。
在这里插入图片描述
我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是 60。
本题的要求就是请你编程判定:对给定的 m \times nm×n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。
如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。
如果无法分割,则输出 0。

输入描述
程序先读入两个整数 m,nm,n 用空格分割 (m,n<10)(m,n<10),表示表格的宽度和高度。
接下来是 nn 行,每行 mm 个正整数,用空格分开。每个整数不大于 10^4。

输出描述
在所有解中,包含左上角的分割区可能包含的最小的格子数目。

import os
import sys

# 请在此输入您的代码
m, n = map(int, input().split())
num = []
target = 0
for i in range(n):
    datain = input().split()
    num.append([int(x) for x in datain])
    target += sum(num[i])
# 算出目标值
target /= 2
# 思路是从左上角开始走格子,每走过一个就加上自己,看能不能凑到一半
# 那么如果不是在边界处,每个格子有四个方向可以选择,下面就定义一下方向
# 分别是下上左右

# 因为数据规模规定最大是10行10列,所以最小格子数目不会超过100,这里定义为101.
maxgezi = 101
direction = [[-1, 0], [1, 0], [0, -1], [0, 1]]

# 定义四个参数,分别是当前坐标x,y。当前数目总和total,计数走过的格子count,vis用来标记走过哪些格子
def dfs(x, y, total, count, vis):
    global maxgezi # 函数内部用到全局变量需要声明这个全局变量
    # 这里就是剪枝啦
    # 如果之前有解,当前解走过的格子比之前还多,那么就没必要走了,返回
    if count >= maxgezi:
        return
    # 如果算到当前格子的总和都超过目标值了,没必要继续走了,返回
    if total + num[y][x] > target:
        return
    # 终止条件判断
    if total + num[y][x] == target:
        # count为什么也要加一呢?是因为当前格子还没算
        maxgezi = min(maxgezi, count + 1)
        return

    for dire in direction:
        next_y = y + dire[0]
        next_x = x + dire[1]
        # 防止越界判断
        if next_x < 0 or next_x >= m or next_y < 0 or next_y >= n:
            continue
        if vis[next_y][next_x] > 0:  # 如果下一个点没访问过,则可以访问
            vis[y][x] -= 1
            dfs(next_x, next_y, total + num[y][x], count + 1, vis)
            vis[y][x] += 1
# 注意python创建二维数组的方式
# 如果是[[x]*m]*n这样创建的话,修改任意行的其中一个数,其他行对应位置的数也会改
# 所以要像下面这样初始化二维数组
dfs(0, 0, 0, 0, [[1] * m for _ in range(n)])
if maxgezi == 101:
    print(0)
else:
    print(maxgezi)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hilbob

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值