到底怎么“接受这个数组”?
本文最后更新于4 天前,其中的信息可能已经过时,如有错误请发送邮件到184874483@qq.com

题目考点

这道题主要考这几个点:

  1. 输入处理
    你要把多行输入,读成一个二维数组。
  2. 二维数组的理解
    题目给的是 N 行 M 列,本质上就是“列表里面放列表”。
  3. NumPy 的基本操作
    这道题不是让你手写转置,而是使用 NumPy 做:
    • transpose(转置)
    • flatten(展平)

所以这道题的真正难点,往往不是 transpose()flatten(),而是前面的这一步:

怎么把输入的数据,正确地读成一个二维数组。


审题

题目给你的是一个 N × M 的整数矩阵。

输入格式是这样的:

第一行:N M
接下来 N 行:每行有 M 个整数

比如样例输入:

2 2
1 2
3 4

它表示:

  • 有 2 行
  • 每行 2 个数

所以这个矩阵其实就是:

[
    [1, 2],
    [3, 4]
]

题目要求输出两部分:

  1. 先输出转置后的数组
  2. 再输出展平后的数组

思路提示

先不要急着记代码,先把过程想清楚。

这道题可以拆成 3 步:

第一步,先读入第一行,拿到 NM
第二步,再读接下来的 N 行,把每一行变成一个列表,最后组成“列表套列表”的二维数组。
第三步,把这个二维数组转成 NumPy 数组,然后调用:

  • numpy.transpose()
  • numpy.flatten()

这里最关键的是第二步。

因为 input() 每次读进来的,其实都是一整行字符串。
比如读到这一行:

1 2

实际上最开始它是字符串 "1 2",不是列表,也不是整数。
所以你要经过:

input().split()

变成:

['1', '2']

再经过:

map(int, ...)

把每个字符串变成整数:

[1, 2]

这样一行一行处理后,最后就得到二维数组。


完整设计思路

第一步:读取行数和列数

第一行输入是:

N M

所以我们可以这样写:

N, M = map(int, input().split())

这句的意思是:

  • input() 读入一整行,例如 "2 2"
  • split() 按空格切开,得到 ['2', '2']
  • map(int, ...) 把两个字符串变成整数
  • 最后分别赋值给 NM

这样:

N = 2
M = 2

第二步:读取矩阵

接下来有 N 行,每一行都是矩阵的一行。

所以我们需要循环 N 次,每次读一行:

arr = []
for _ in range(N):
    row = list(map(int, input().split()))
    arr.append(row)

这里的 arr 一开始是空列表。

假设输入是:

1 2
3 4

那么过程是:

第一次循环后:

arr = [[1, 2]]

第二次循环后:

arr = [[1, 2], [3, 4]]

这时候,它就已经是一个标准的二维数组结构了。


第三步:转成 NumPy 数组

上面的 arr 还是 Python 的普通列表。
而题目要用 NumPy 来做转置和展平,所以要先转成 NumPy 数组:

import numpy
arr = numpy.array(arr)

此时:

arr

就变成了 NumPy 的二维数组。


第四步:输出转置和展平

转置:

print(numpy.transpose(arr))

或者也可以写:

print(arr.transpose())

展平:

print(arr.flatten())

代码实现

下面给你一个适合初学者理解的完整版本:

import numpy

N, M = map(int, input().split())

arr = []
for _ in range(N):
    row = list(map(int, input().split()))
    arr.append(row)

arr = numpy.array(arr)

print(numpy.transpose(arr))
print(arr.flatten())

运行演示

我们用题目的样例来手动走一遍。

输入:

2 2
1 2
3 4

第一步:读第一行

N, M = map(int, input().split())

读到:

N = 2
M = 2

第二步:读矩阵

一开始:

arr = []

第一次循环读到:

1 2

处理后:

row = [1, 2]
arr = [[1, 2]]

第二次循环读到:

3 4

处理后:

row = [3, 4]
arr = [[1, 2], [3, 4]]

第三步:转成 NumPy 数组

arr = numpy.array(arr)

现在相当于:

[[1 2]
 [3 4]]

第四步:转置

原矩阵是:

[[1 2]
 [3 4]]

转置后,行列互换:

[[1 3]
 [2 4]]

第五步:展平

展平就是把二维数组按顺序拉成一维:

[1 2 3 4]

最关心的问题:到底怎么“接受这个数组”?

这个问题非常关键。可以把“接受数组”理解成:

把输入的多行数字,存进程序里的变量。

这道题的接收方式就是:

接收一行数字

row = list(map(int, input().split()))

比如输入:

1 2 3

执行后:

row = [1, 2, 3]

接收整个二维数组

arr = []
for _ in range(N):
    row = list(map(int, input().split()))
    arr.append(row)

比如输入:

1 2
3 4

最后:

arr = [
    [1, 2],
    [3, 4]
]

所以可以记住这个固定模板:

arr = []
for _ in range(N):
    row = list(map(int, input().split()))
    arr.append(row)

以后遇到“输入一个矩阵”“输入一个二维数组”的题,十有八九都可以先想到这个模板。


再补一个理解点:为什么不是直接 input() 就行?

因为 input() 读到的是字符串。

例如输入:

1 2

你直接写:

x = input()
print(x)
print(type(x))

得到的是:

1 2
<class 'str'>

这说明它只是一个字符串,不是列表,也不是整数。

所以必须进一步处理:

split()

把字符串按空格切开:

input().split()

结果:

['1', '2']

注意,这仍然是字符串列表。

map(int, ...)

把每个字符串转成整数:

map(int, ['1', '2'])

再用 list() 变成列表:

[1, 2]

所以:

list(map(int, input().split()))

就是读一行整数最常见、最重要的写法之一。


方法总结

以后你看到这种题,判断方法可以很固定:

如果题目输入是“单行多个数”

就优先想到:

list(map(int, input().split()))

如果题目输入是“矩阵 / 二维数组 / N 行 M 列”

就优先想到:

arr = []
for _ in range(N):
    row = list(map(int, input().split()))
    arr.append(row)

如果题目还要求 NumPy 操作

再补一步:

arr = numpy.array(arr)

这就是这类题最核心的套路。


本节小结

这道题本身不难,真正要掌握的是“输入如何存成二维数组”。

你需要记住两层结构:

第一层,一行数字怎么读:

row = list(map(int, input().split()))

第二层,多行组成二维数组怎么读:

arr = []
for _ in range(N):
    row = list(map(int, input().split()))
    arr.append(row)

当你把这一步掌握了,后面的 transpose()flatten() 就只是工具调用了。


练习

你先做这一题,不看答案,自己练一下输入接收。

题目:

输入一个 3 × 2 的整数矩阵,输出它原本的二维列表结构。

样例输入:

3 2
1 2
3 4
5 6

你只需要输出:

[[1, 2], [3, 4], [5, 6]]

提示:

  1. 第一行先读出 NM
  2. 用循环读 N
  3. 每次把一行变成整数列表
  4. append() 放进总列表里

row = list(map(int, input().split()))这句代码到底在做什么

我们先把整句放出来:

row = list(map(int, input().split()))

这句代码的作用只有一句话:

把用户输入的一行数字,按空格拆开,并把每个数字从字符串变成整数,最后存成一个列表。

比如用户输入:

1 2 3 4

执行完这句后,row 就会变成:

[1, 2, 3, 4]

注意,最后得到的是一个“整数列表”,不是字符串,也不是一个大字符串。


先看结果,再反推过程

很多初学者一看到这句会觉得太长。
其实它只是把 4 个小动作连在了一起。

你可以把它拆成这样看:

temp1 = input()
temp2 = temp1.split()
temp3 = map(int, temp2)
row = list(temp3)

这 4 步合起来,才是原来那一句。

所以你真正要理解的,就是这 4 步分别做了什么。


第一步:input() 做了什么

作用

input() 会读取用户输入的一整行内容。

比如你运行程序,用户输入:

1 2 3 4

那么:

temp1 = input()

此时 temp1 的值是:

'1 2 3 4'

注意这里最关键的一点:

input() 读进来的永远是字符串 str

哪怕你输入的是数字,它读进来以后仍然是字符串。

比如:

x = input()
print(x)
print(type(x))

如果你输入:

123

输出会是:

123
<class 'str'>

所以你要先牢牢记住:

input() 不会自动帮你变成整数。


第二步:split() 做了什么

我们继续看:

temp2 = temp1.split()

假设:

temp1 = '1 2 3 4'

那么执行后:

temp2 = ['1', '2', '3', '4']

split() 的本质

split() 的作用是:

把一个字符串按某种分隔方式切开,变成列表。

默认情况下,不写参数时,它按“空白字符”切分。
空白字符包括:

  • 空格
  • 制表符
  • 换行等

所以:

'1 2 3 4'.split()

结果是:

['1', '2', '3', '4']

注意一个非常容易忽略的点

现在虽然已经变成列表了,但里面的元素还是字符串,不是整数。

也就是说:

['1', '2', '3', '4']

不是:

[1, 2, 3, 4]

你可以验证一下:

data = input().split()
print(data)
print(type(data))
print(type(data[0]))

如果输入:

1 2 3

那么:

  • data 是列表 list
  • data[0] 是字符串 str

所以 split() 只是“切开”,不负责类型转换


第三步:map(int, temp2) 做了什么

这是很多初学者最容易卡住的一步。

temp3 = map(int, temp2)

假设:

temp2 = ['1', '2', '3', '4']

那么这句的意思是:

temp2 里面的每一个元素,都拿去执行一次 int()

也就是相当于:

int('1')
int('2')
int('3')
int('4')

结果就会变成整数:

1, 2, 3, 4

你可以把 map 理解成“批量处理”

它的格式是:

map(函数, 可迭代对象)

这里:

  • 函数是 int
  • 可迭代对象是 ['1', '2', '3', '4']

所以它的意思就是:

把列表中的每个字符串,都交给 int 去处理。


第四步:list(...) 做了什么

在 Python 3 里,map() 返回的不是最终列表,而是一个 map 对象。

所以如果你直接写:

temp3 = map(int, ['1', '2', '3'])
print(temp3)

你看到的可能是这种结果:

<map object at 0x...>

这不是我们想要的最终数据。

所以要再套一层 list()

row = list(temp3)

这样才真正得到:

[1, 2, 3]

所以 list() 的作用就是:

map 处理后的结果,真正收集成一个列表。


从里到外,完整执行顺序

现在我们把原句重新看一遍:

row = list(map(int, input().split()))

它不是从左到右先执行 list,而是从里面往外执行。

实际顺序是:

第 1 层:先执行 input()

用户输入:

10 20 30

得到:

'10 20 30'

第 2 层:再执行 .split()

['10', '20', '30']

第 3 层:再执行 map(int, ...)

把每个字符串变成整数:

10, 20, 30

第 4 层:最后执行 list(...)

得到最终结果:

[10, 20, 30]

赋值给:

row

所以最后:

row = [10, 20, 30]

用一个完整例子手动模拟

下面我们手动模拟一遍,非常重要。

代码:

row = list(map(int, input().split()))
print(row)
print(type(row))
print(type(row[0]))

假设输入:

7 8 9

第一步

input()

读到的是:

'7 8 9'

类型是:

str

第二步

input().split()

得到:

['7', '8', '9']

类型是:

list

但里面每个元素的类型仍然是:

str

第三步

map(int, ['7', '8', '9'])

相当于:

int('7'), int('8'), int('9')

结果是:

7, 8, 9

第四步

list(...)

得到:

[7, 8, 9]

所以输出是:

[7, 8, 9]
<class 'list'>
<class 'int'>

为什么要这样写,不能直接用 input()

不能,因为 input() 只会给你一整行字符串。

比如你想得到:

[1, 2, 3]

但你直接写:

row = input()

用户输入:

1 2 3

那么 row 得到的是:

'1 2 3'

它只是一个字符串。

这时你不能把它当成数字列表来用。

比如:

row[0]

得到的是字符 '1',不是整数 1

如果输入是:

12 34 56

row[0] 得到的是字符 '1',更能看出问题。

所以必须经过:

  1. split() 切成多个部分
  2. int() 把每部分转成整数
  3. list() 收集成列表

这句代码的等价写法

如果你觉得原写法太紧凑,可以先写成更容易看懂的版本:

line = input()
parts = line.split()
row = []

for x in parts:
    row.append(int(x))

这和下面这句是一个意思:

row = list(map(int, input().split()))

所以你可以这样理解:

简写版

row = list(map(int, input().split()))

展开版

line = input()
parts = line.split()
row = []

for x in parts:
    row.append(int(x))

对于初学者来说,先理解展开版,再回头看简写版,会轻松很多。


再进一步:为什么 map(int, ...) 可以这样写

这里有一个很重要的语法思想:

map(函数, 数据)

它表示:

把“函数”应用到“数据”的每一个元素上。

例如:

例 1:把字符串变整数

list(map(int, ['1', '2', '3']))

结果:

[1, 2, 3]

例 2:把字符串变浮点数

list(map(float, ['1.5', '2.8', '3']))

结果:

[1.5, 2.8, 3.0]

例 3:把整数变字符串

list(map(str, [1, 2, 3]))

结果:

['1', '2', '3']

所以 map 并不是只会配合 int,它是一个“批量套函数”的工具。


常见错误

错误 1:忘了 split()

错误写法:

row = list(map(int, input()))

假设输入:

12 34 56

这里 input() 返回的是字符串 "12 34 56"
如果直接对这个字符串做 map(int, ...),它会按字符一个一个处理,相当于在做:

int('1')
int('2')
int(' ')
int('3')
int('4')
...

一遇到空格就会报错。

所以必须先 split()


错误 2:只 split(),不转整数

错误写法:

row = input().split()

结果是:

['1', '2', '3']

它看起来很像数字列表,但其实不是。

这会导致很多逻辑错误,比如:

row[0] + row[1]

不是数字相加,而是字符串拼接。

例如:

'1' + '2'   ->   '12'

这和你想要的 3 完全不同。


错误 3:忘了套 list()

错误写法:

row = map(int, input().split())
print(row)

你看到的不会是普通列表,而是一个 map 对象。

虽然它不是完全不能用,但对初学者来说不直观。
在做题时,通常直接套 list() 最清楚。


这句模板在题目里怎么用

情况 1:读一行整数

比如输入:

1 2 3 4 5

写法:

row = list(map(int, input().split()))

结果:

[1, 2, 3, 4, 5]

情况 2:读一行浮点数

比如输入:

88.5 90.0 76.5

写法:

scores = list(map(float, input().split()))

结果:

[88.5, 90.0, 76.5]

情况 3:读矩阵中的一行

比如矩阵有 N 行,每行都是整数:

arr = []
for _ in range(N):
    row = list(map(int, input().split()))
    arr.append(row)

这里的 row 就是矩阵的一行。


你可以怎样判断自己是不是真的懂了

你现在可以自己问自己这 4 个问题:

问题 1

input() 返回什么类型?

答案应该是:str

问题 2

split() 之后得到什么?

答案应该是:字符串列表

问题 3

map(int, ...) 在干什么?

答案应该是:把列表里每个字符串都转成整数

问题 4

为什么最后还要 list()

答案应该是:把 map 的结果真正变成列表

如果这 4 个问题你都能答清楚,这句模板你就已经基本掌握了。


方法总结

把这句代码彻底压缩成一句最核心的话就是:

row = list(map(int, input().split()))

等于:

读一行 → 按空格切开 → 每个元素转成整数 → 收集成列表

你以后遇到“输入一行多个整数”的题,几乎都可以先想到这句。

你可以把它记成固定模板:

输入一行整数

row = list(map(int, input().split()))

输入一行浮点数

row = list(map(float, input().split()))

输入矩阵

arr = []
for _ in range(N):
    row = list(map(int, input().split()))
    arr.append(row)

本节小结

初学者最容易把这句看成一个整体,然后觉得难。
其实你只要愿意拆开,它本质上只是 4 个小动作:

line = input()          # 读入整行字符串
parts = line.split()    # 按空格切开,得到字符串列表
nums = map(int, parts)  # 每个字符串转成整数
row = list(nums)        # 变成真正的整数列表

所以以后遇到这种写法,不要害怕,第一反应就是:

从里往外拆。


练习

先自己做这道小练习,不要急着看答案。

题目:

用户输入一行数据:

10 20 30 40

请把它读成整数列表,然后输出:

[10, 20, 30, 40]

提示:

  1. 先用 input() 读一整行
  2. split() 切开
  3. map(int, ...) 转整数
  4. list() 变成列表

补充说明:本题里 m 能不能不赋值

可以先给你一个非常准确的结论:

本题里,m 可以“不参与后续逻辑”,但不能“完全不读”。

这是两个不同的概念,你一定要分清。


一、什么叫“可以不用 m”

像这道题,常见写法是:

n, m = map(int, input().split())

然后后面只写:

arr = []
for _ in range(n):
    row = list(map(int, input().split()))
    arr.append(row)

你会发现:

  • n 用来控制读多少行
  • m 好像没有继续写进代码里

所以从“后续逻辑是否依赖它”来说,这题里的 m 确实可以不用。

也就是说,你后面不一定非要写:

if len(row) != m:

也不一定非要写双重循环去用它。


二、什么叫“不能完全不读”

虽然 m 在后续逻辑中可以不用,但第一行输入里本来就有两个数:nm

比如输入:

2 2
1 2
3 4

第一行是:

2 2

这表示这一行里有两个值,不是一个值。

所以程序在读取第一行时,必须把这一行处理掉。


三、最容易混淆的点:不用 m,不等于不读取 m

这是你后续复习最容易疑惑的地方,我单独强调一下。

情况 1:读取了 m,但后面没使用

这是可以的。

例如:

n, m = map(int, input().split())

后面只用 n,不用 m
这种写法完全没问题。


情况 2:第一行只想读 n,连 m 都不处理

这就要小心。

你不能直接写:

n = int(input())

因为第一行输入是:

2 2

而不是单独一个:

2

所以这句会报错。因为:

int("2 2")

这是不合法的。


四、所以到底能不能“只对 n 赋值”

能,但前提是:

你仍然要把第一行完整读进来,只是第二个值不保存,或者保存后不用。

这和“完全不读 m”不是一回事。


五、几种正确写法

写法 1:最推荐,最清楚

n, m = map(int, input().split())

优点是:

  • 和题目格式完全对应
  • 读代码的人一眼就知道输入是 n m
  • 后续即使暂时不用 m,也不影响理解

这也是最适合初学者、最适合复习的写法。


写法 2:明确表示“第二个值我读了,但不用”

n, _ = map(int, input().split())

这里的 _ 是一种约定俗成的写法,意思是:

这个值我接收了,但我不打算使用。

这就非常适合这种情况:

  • 第一行确实有两个值
  • 我必须把它们读出来
  • 但后面逻辑只需要 n

这比直接硬省略更规范。


写法 3:手动取第一个值

first_line = input().split()
n = int(first_line[0])

这也能工作,因为:

  • input() 已经把整行读走了
  • split() 已经把两个值都拆开了
  • 只是你只取了第一个

但这种写法不如前两种直观。

因为别人看代码时,会疑惑:

“题目明明有两个值,为什么只取一个?是不是漏了?”

所以这更适合你在特别明确的情况下使用,不适合作为初学者默认模板。


六、最不推荐的误解写法

错误理解:既然 m 没用,那我就当第一行只有一个数

比如有人会想写成:

n = int(input())

这在本题里不行。

因为第一行根本不是单独一个整数,而是两个整数在同一行。

样例第一行是:

2 2

不是:

2

所以这时候,不是“m 没用”,而是“输入格式已经规定这一行有两个值”。

程序必须按真实输入格式来读。


七、你可以这样理解:读取输入 和 使用变量 是两回事

这个区分特别重要。

第一步:读取输入

你要先保证程序把题目给的数据正确读进来。

题目说第一行有两个数,你就应该按两个数去读。

例如:

n, m = map(int, input().split())

或者:

n, _ = map(int, input().split())

第二步:使用变量

读进来之后,你再决定后面逻辑到底要不要用 m

这题里通常只需要:

  • n 控制循环次数
  • 不额外用 m

所以是:

读了 m,但不一定要在后续逻辑中使用 m


八、为什么本题里 m 常常可以不用

因为这道题的输入方式是“按行输入矩阵”。

你写的是:

for _ in range(n):
    row = list(map(int, input().split()))
    arr.append(row)

每次 input() 直接读一整行。
而平台保证每一行正好有 m 个元素,所以你不用自己再数一遍。

因此,m 在这题里更像是:

  • 一个输入格式说明
  • 一个维度信息
  • 一个理论上可用于校验的值

而不是必须参与后续计算的核心变量。


九、那为什么很多标准答案还是会写 n, m

因为这样更完整、更规范。

原因有 3 个。

1. 和题目输入格式对应

题目第一行就是两个数。
你写:

n, m = map(int, input().split())

最符合题意。


2. 方便以后扩展

现在这题里 m 可能没用。
但如果以后题目要求你检查输入长度,或者用 reshape(n, m),那 m 就有用了。

保留它,代码扩展更自然。


3. 复习时不容易误会

如果你复习时只看到:

n, _ = map(int, input().split())

你还能知道原来第一行有两个值。

但如果你只写成某种非常简略的形式,后面再看就可能忘掉题目的完整输入结构。

所以从“适合长期复习”的角度说,保留 m 更稳妥。


十、这题到底推荐怎么写

如果你是为了学习、为了以后复习,我建议默认写成:

import numpy

n, m = map(int, input().split())

arr = []
for _ in range(n):
    row = list(map(int, input().split()))
    arr.append(row)

arr = numpy.array(arr)
print(numpy.transpose(arr))
print(arr.flatten())

这里虽然 m 没有被后续代码显式使用,但这份代码的优点是:

  • 输入结构完整
  • 可读性高
  • 不容易让你以后误解“第一行到底有几个值”

十一、如果你就是想明确表示“m 不使用”,可以这样写

import numpy

n, _ = map(int, input().split())

arr = []
for _ in range(n):
    row = list(map(int, input().split()))
    arr.append(row)

arr = numpy.array(arr)
print(numpy.transpose(arr))
print(arr.flatten())

这也对。

但这里有两个 _

  • 第一个 _ 表示“第二个输入值我不使用”
  • 第二个 _ 表示“循环变量我不使用”

对初学者来说,两个 _ 放在一起有时会有点绕。
所以学习阶段我更建议你保留 m,更直观。


十二、一句话帮你彻底记住

你可以记这句:

本题里,m 可以不参与后续计算,但不能把它当作不存在。因为输入第一行本来就有它。

再压缩成更短一句:

可以“不用 m”,但不能“不读 m”。


本节小结

你这个问题非常值得单独补充,因为以后复习时确实很容易混淆。

这道题里:

  • n 真正决定循环次数,因为要读 n
  • m 常常不参与后续逻辑,所以看起来“没用”
  • 但第一行输入格式是 n m
  • 所以程序必须把这两个值所在的那一行正确读出来

因此最规范的理解应该是:

m 在本题里可以是“读了但暂时不用”的变量,而不是“根本不存在的变量”。


练习

你自己先判断下面三种写法,哪种在本题里是合理的,哪种不合理。

写法 A

n, m = map(int, input().split())

写法 B

n, _ = map(int, input().split())

写法 C

n = int(input())

提示:

  • 先想题目的第一行到底长什么样
  • 再想“读取输入”和“后续是否使用”是不是一回事

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇