ccf-csp 2024-6-2 python

矩阵重塑(其二)

刷新 

时间限制: 1.0 秒

空间限制: 512 MiB

相关文件: 题目目录(样例文件)

题目背景

矩阵转置操作是将矩阵的行和列交换的过程。在转置过程中,原矩阵 AA 的元素 aijaij​ 会移动到转置后的矩阵 ATAT 的 ajiaji​ 的位置。这意味着 AA 的第 ii 行第 jj 列的元素在 ATAT 中成为了第 jj 行第 ii 列的元素。

例如,有矩阵 AA 如下:

A=[abcdef]A=[ad​be​cf​]

它的转置矩阵 ATAT 会是:

AT=[adbecf]AT=​abc​def​​

矩阵转置在线性代数中是一个基本操作,广泛应用于各种数学和工程领域。

题目描述

给定 n×mn×m 的矩阵 MM,试编写程序支持以下查询和操作:

  1. 重塑操作 pp、qq:将当前矩阵重塑为 p×qp×q 的形状(重塑的具体定义见上一题);

  2. 转置操作:将当前矩阵转置;

  3. 元素查询 ii、jj:查询当前矩阵第 ii 行 jj 列的元素(0≤i<n0≤i<n 且 0≤j<m0≤j<m)。

依次给出 tt 个上述查询或操作,计算其中每个查询的结果。

输入格式

从标准输入读入数据。

输入共 n+t+1n+t+1 行。

输入的第一行包含三个正整数 nn、mm 和 tt。

接下来依次输入初始矩阵 MM 的第 00 到第 n−1n−1 行,每行包含 mm 个整数,按列下标从 00 到 m−1m−1 的顺序依次给出。

接下来输入 tt 行,每行包含形如 op a b 的三个整数,依次给出每个查询或操作。具体输入格式如下:

  • 重塑操作:1 p q

  • 转置操作:2 0 0

  • 元素查询:3 i j

输出格式

输出到标准输出。

每个查询操作输出一行,仅包含一个整数表示查询结果。

样例1输入

3 2 3
1 2
3 4
5 6
3 0 1
1 2 3
3 1 2

 

样例1输出

2
6

 

样例2输入

3 2 5
1 2
3 4
5 6
3 1 0
2 0 0
3 1 0
1 3 2
3 1 0

 

样例2输出

3
2
5

 

初始矩阵: [123456]​135​246​​, (1,0)(1,0) 位置元素为 33;

转置后: [135246][12​34​56​], (1,0)(1,0) 位置元素为 22;

重塑后: [135246]​154​326​​, (1,0)(1,0) 位置元素为 55。

子任务

8080 的测试数据满足:

  • t≤100t≤100;

全部的测试数据满足:

  • t≤105t≤105 且其中转置操作的次数不超过 100100;

  • nn、mm 和所有重塑操作中的 pp、qq 均为正整数且 n×m=p×q≤104n×m=p×q≤104;

  • 输入矩阵中每个元素的绝对值不超过 10001000。

提示

  • 对于 n×mn×m 的矩阵,虽然转置和重塑操作都可以将矩阵形态变为 m×nm×n,但这两种操作通常会导致不同的结果。

  • 评测环境仅提供各语言的标准库,特别地,不提供任何线性代数库(如 numpypytorch 等)。

 

-------------------------------------------------------

 

 

         分析:这题有点绕。

        如果只想拿80%通过,很简单,暴力就可以。

        忘记是哪一年的认证了,也用到了矩阵转置,非常兴奋,一顿操作猛如虎。我也注意到了一些可以优化的地方:

                1、连续偶数次转置等于没转;

                2、因为可以化为一维,所以所有的重置矩阵的一维不变的, 所以连续重置的话只用看最后一次就行了。

        按照这个优化思路,一直是80%,非常痛苦。

        有兴趣可以看一下最后的版本:

# 定义重塑操作函数
def rebuild(M:list , p :int ,q:int):
    n = len(M)
    m = len(M[0])
    # 转化为一维矩阵A
    A = []
    for i in range(n):
        for j in range(m):
            A.append( M[i][j])

    # 转化为新矩阵M_1
    i =0
    M_1 = []
    while( i<= (len(A) - q)):
        j = i+ q-1
        M_1.append(list( A[i:j+1] ))
        i += q

    return M_1

# 定义矩阵转置操作
def T( M:list):
    return [  [row[i] for row in M] for i in range(len(M[0]))  ]


##########主程序############################
n , m , t = map( int, input().split() )

# 输入原始矩阵
M = []
for _ in range(n):
    M.append( list( map(int , input().split() ) ) )

# 分析:
# 矩阵的转置,是一个n复杂度的操作
# 矩阵的重塑:是一个n复杂度的操作,n平均在10^2次
# 重塑和转置操作本身优化不了了
# 因此有两个优化方向:1,减少转置操作次数;2减少重塑操作次数
# 此时要注意到,题目说了t最大可以到10^5,但是转置次数不超过100,所以重置操作最大可以到10^5-100次,让复杂度变成n^3
#       因此优化的重点在减少重塑的次数,应当注意到,如果有连续的重塑操作,所有连续的重塑操作中的一维数组是不变的,
#       从而得到这一些连续的重塑操作只用做最后一次的重塑就可以了

# 如果单单考虑重塑,会发现还是超时,所以再加上转置的考虑,如果转置偶次数,相当于没转,那么就不用算终止重塑了

# 开始输入操作
flag_r = False # 记录重塑是否连续

number_t = 0 # 记录连续的转置次数

pre_deed = [] # 前一次操作

deeds = []  # 记录所有操作

for _ in range(t):
    deeds.append( list( map(int , input().split() ) ) )

i = 0
while( i < t ):
    
    if deeds[i][0] == 1:  # 重塑操作,首先考虑的因素
        flag_r = True
        # 记录好本次重塑操作
        pre_deed = deeds[i]
        i += 1
        continue
    
    else:   # 重塑操作中断了,开始考虑转置的连续
            
        if deeds[i][0] == 2:    # 转置操作

            if flag_r == True:     # 前一个操作是重塑,开始判断有没有断开重塑操作
                while( deeds[i][0] == 2):
                    number_t += 1
                    i += 1
                # 这里一定要注意while结束时i已经是下一位了
                
                if number_t % 2 != 0:   # 基数次转置操作,重塑被中断了
                    flag_r = False
                    numbers = 0
                    # 先重塑
                    M = rebuild(M, pre_deed[1] , pre_deed[2])

                    # 再转置
                    M = T(M)

                    if deeds[i][0] == 1:   # 重塑
                        falg_r = True
                        i+=1
                        continue

                    else:   # 说明deeds[i][0] ==3是查询操作
                        print(M[ deeds[i][1] ][ deeds[i][2] ] )
                        i+=1
                        continue
                        
                else:   #偶数次转置操作,不需要转置,重塑没有中断
                    if deeds[i][0] == 1:    # “再续前缘”,继续重塑
                        pre_deed = deeds[i]
                        i+= 1
                        numbers = 0
                        continue

                    else:  # 说明deed[0] == 3,是查询操作
                        # 先重塑
                        M = rebuild(M, pre_deed[1] , pre_deed[2])
                        # 再查询
                        print(M[ deeds[i][1] ][ deeds[i][2] ] )
                        
            else:   # 前一个操作不是重塑,直接转置
                M = T(M)
                i+=1
                continue

        else: # 说明 deed[0] == 3:
            if flag_r == True:  # 前一个操作是重塑
                M = rebuild(M, pre_deed[1] , pre_deed[2]) # 先重塑
                flag_r = False
                print(M[ deeds[i][1] ][ deeds[i][2] ] ) # 再查询
            else:
                print(M[ deeds[i][1] ][ deeds[i][2] ] )

            i+=1

       5c687aff319742948257fb5ccac25606.png

        经历了顶级折磨,我发现,其实最大的时间复杂度,就是转置和重置操作本身,所以想要真正的优化时间复杂度,就要尽量不做这些操作。

        继续从一维的A矩阵出发,发现重置操作其实可以直接跳过(A不会变),这就直接省了绝大部分时间复杂度(重置操作可能特别多10^5-100)。

        所以实际需要操作的只有转置(最多200次),这就快很多了。但是难点在于,转置要基于一维的A来进行。具体的思路见代码和注释。

# 分析:
# 矩阵的转置,是一个nm复杂度的操作
# 矩阵的重塑:是一个nm复杂度的操作,nm最大在10^4 ,很大,所以能不能直接跳过呢?
# 矩阵的输出:复杂度为1,不用考虑优化
#   重塑和转置操作本身优化不了
#   如果老老实实的遍历,每个输入都操作,最大会有nmt = nm^2的复杂度
        # 因此有两个优化方向:1,减少转置操作次数;2减少重塑操作次数
        #       此时要注意到,题目说了t最大可以到10^5,但是转置次数不超过100,所以重置操作最大可以到10^5-100次,让复杂度变成(nm)^2
        #       因此优化的重点在减少重塑的次数,应当注意到,如果有连续的重塑操作,所有连续的重塑操作中的一维数组是不变的,
        #       因此重塑操作不用管,只用记住最后一次重塑操作的p,q就行了,这个p,q会在转置的时候用到

        #       而对于转置操作,如果同样从一维A出发,考虑如何把A 矩阵变成转置矩阵的一维矩阵A_2就行了,复杂度nm

        #       查询的话,简单,就是在一维A中查询下标为i*q +j的元素就行了

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

M =[]
for _ in range(n):  # 输入矩阵M
    M.append( list( map(int ,input().split() ) ))

#把M降成一维A
A = []
for i in range(n):
    for j in range(m):
        A.append(M[i][j])

#print(A)
# 开始处理操作
i = 0
m_shape = [ n ,m]  # 用于记录p,q即最后的矩阵形式的行和列,初始值就是原M的行列
while( i<t ):
    deed = list( map(int ,input().split() ) )

    if deed[0] == 1:        #重塑操作,不用管,记录p,q就行,
        m_shape = [deed[1],deed[2]]
        i+=1
        continue
    elif deed[0] == 2:       # 转置操作
        # 考虑如何将一维的A化为转置后矩阵的一维矩阵A_t
        A_t = []
        
        # 原来是m_shape[0] * m_shape[1],转置为m_shape[1],m_shape[0]
        m_shape = [m_shape[1],m_shape[0]]
        
        for x in range(m_shape[0]):   # p,行数
            for y in range(m_shape[1]):   # q,列数
                #print(A[ x + y*pre_deed[2] ])
                A_t.append( A[ x + y*m_shape[0]  ])   # y*原来的列数

        #print(A_t , len(A_t))
       
        A = A_t
        i += 1

    else:  # 输出操作
        print(A[ m_shape[1]*deed[1]  + deed[2] ] )
        i += 1
   

04ee04acdf944f8a96d2eb9e212a32e1.png

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值