CSP认证2023-05:重复局面、矩阵压缩、解压缩,python满分解答代码

CSP认证2023-05:重复局面、矩阵压缩、解压缩,python满分解答代码

目录

一、重复局面

问题描述

​输入和输出

思路 

代码和结果

二、矩阵压缩

问题描述

输入和输出

思路 

代码和结果 

三、解压缩

问题描述

输入和输出

思路 

代码和结果 


一、重复局面

问题描述

3a1b1606105445d1ad7be0ec0f4ebecd.png

4eb73ea98b9d4836b1d82e37782524bd.png 输入和输出

345c08c4b96c451ca119d267d3f82f5b.png

输入

8
********
******pk
*****r*p
p*pQ****
********
**b*B*PP
****qP**
**R***K*
********
******pk
*****r*p
p*pQ****
*b******
****B*PP
****qP**
**R***K*
********
******pk
*****r*p
p*p*****
*b**Q***
****B*PP
****qP**
**R***K*
******k*
******p*
*****r*p
p*p*****
*b**Q***
****B*PP
****qP**
**R***K*
******k*
******p*
*****r*p
p*pQ****
*b******
****B*PP
****qP**
**R***K*
********
******pk
*****r*p
p*pQ****
*b******
****B*PP
****qP**
**R***K*
********
******pk
*****r*p
p*p*****
*b**Q***
****B*PP
****qP**
**R***K*
********
******pk
******rp
p*p*****
*b**Q***
****B*PP
****qP**
**R***K*

输出 

1
1
1
1
1
2
2
1

思路 

将每8行的数据拼接成一个大的字符串,然后将该字符串存放到字典中,每次对字典进行查询即可。

代码和结果

m = 8
n = int(input())
res = {}
for i in range(1,n+1):
    jumian = ''
    for j in range(8):
        strings = input()
        jumian+=strings
    if jumian not in res.keys():
        res[jumian]=1
        print(1)
    else:
        res[jumian] += 1
        print(res[jumian])

220f036d73134e948166a6a37c727c02.png

二、矩阵压缩

问题描述

5d426a981bbd40e7b1e61cfe4fce0d4b.png

输入和输出

 507d35874bcf4b81a7896a67f2d08e03.png

输入

3 2
1 2
3 4
5 6
10 10
-20 -20
30 30
6 5
4 3
2 1
4 0 -5

输出 

480 240
0 0
-2200 -1100

思路 

本题就是做一个矩阵的乘法运算,然而矩阵的乘法复杂度为O(eq?n%5E3),其中n是和矩阵的维度有关的。为此,可以先分析一下各矩阵的维度:

W :1 x N
Q :N x d
K :N x d
V :N x d

如果按照公式给的顺序进行计算,先算eq?Q%20%5Ctimes%20K%5ET,矩阵维度变化为N x d X d x N => N x N,时间复杂度为O(N^2*d),然而测试用例中n的是很大的,取n和d的最大值代入复杂度进行计算可得2x10^9。但是算法题时间复杂度一般超过10^9或10^8就会超时,所以这样是行不通的。

通过观察,发现d的维度很小,如果转换一下计算顺序就能大大减少时间复杂度。即先算eq?WQ,再算eq?K%5ET%20V,最后将两者相乘。

13859afc28d74b18ab53016d936534ed.png

代码和结果 

n,d = list(map(int,input().split()))
k = []
q = []
v = []
w = []
for i in range(n):  # Q
    q.append(list(map(int,input().split())))
for i in range(n):  # K
    k.append(list(map(int,input().split())))
for i in range(n):  # V
    v.append(list(map(int,input().split())))
w=list(map(int,input().split()))
# 先算W.Q
for i in range(n):
    q[i] = [w[i]*j for j in q[i]]
#然后对K V进行相乘
kv = []
for j in range(d):
    kv_j = []
    for m in range(d):
        x_j_i = 0  # 第j行第i列的数据
        for i in range(n):
            x_j_i +=k[i][j]*v[i][m]
        kv_j.append(x_j_i)
    kv.append(kv_j)
for i in range(n):
    qkv = []
    for j in range(d):
        x_i_j = 0
        for m in range(d):
            x_i_j += q[i][m]*kv[m][j]
        print(x_i_j,end=' ')
    print('')

520090218a764bb3aab6a8515fa0cba1.png

三、解压缩

问题描述

3e1079cadf2047cd9949c878ba5cce11.png

 。。。。。。

输入和输出

0cb6a7b0e00241dbbc44a18d3508fcf9.png

输入

81
8001240102030405
060708090af03c00
0102030405060708
090a0b0c0d0e0f01
0203040506070809
0a0b0c0d0e0f0102
030405060708090a
0b0c0d0e0f010203
0405060708090a0b
0c0d0e0fc603000d
78

 输出

0102030405060708
090a000102030405
060708090a0b0c0d
0e0f010203040506
0708090a0b0c0d0e
0f01020304050607
08090a0b0c0d0e0f
0102030405060708
090a0b0c0d0e0f0d
0e0f0d0e0f0d0e0f
0d0e0f0d0e0f0d0e
0f0d0e0f0d0e0f0d
0e0f0d0e0f0d0e0f
0d0e0f0d0e0f0d0e
0f0d0e0f0d0e0f0d
0e02030405060708

77a419a316dd4d06849dcff4119320de.png

思路 

题目文字量较大,数据的长度也较多,所以需要多看几次题目的问题描述。同时对照题给的输入和输出以及样例说明就能大致明白题给要求了,下面介绍一下问题描述中的几个关键点:

1.文中数据涉及到进制之间的转换,例如:

  • 十六进制转二进制:
def six_to_bin(data):
    data_i = format(int(data,16),'#010b')
    return data_i[2:]
  • 二进制转十进制:
    def bin_to_ten(data):
        return int(data,2)
  • 十六进制转十进制
def six_to_ten(data):
    return int(data,16)

2.小端序:例如两个字节 0x01 0x0A,按小端序组成了十六进制数 0x0A01。在读数据的时候,我们是先读0x01,再读0x0A,但是数据组合时,0x0A是放在前面的,所以当程序运行到此处时,应该倒序拼接,用以下代码即可解决:

 xiaodaun_xu = ''
 for _ in range(num):#小端序
     xiaodaun_xu = raw_datas[start:start+2]+xiaodaun_xu
 start+=2

3.回溯引用,当元素首字节的最低两位是 10或01 时,表示这是一个回溯引用 <O,L>,为此可以定义一个函数实现它,代码如下:

def huisui_yingyong(O,L,data):#data为已输出的数据
    #需要 重复 输出L个字节,为此对L和O进行求余
    m = int(L / O)
    n = L % O
    result = m * data[-O * 2:] + data[-2 * O:-2 * O + 2 * n]
    return result

代码和结果 

import math
def six_to_bin(data):
    data_i = format(int(data,16),'#010b')
    return data_i[2:]
def bin_to_ten(data):
    return int(data,2)
def huisui_yingyong(O,L,data):#data为已输出的数据
    #需要 重复 输出L个字节,为此对L和O进行求余
    m = int(L / O)
    n = L % O
    result = m * data[-O * 2:] + data[-2 * O:-2 * O + 2 * n]
    return result
result = ''
n = math.ceil(int(input())/8)
raw_datas = ''#将数据合并成一个大字符串
for i in range(n):
    input_data = input()
    raw_datas+=input_data
start = 0#开始读取数据
#128 的次方 c_index
c_index = 0
#原始数据的长度
raw_length = 0
while True:
    byte_i = raw_datas[start:start+2]#读取两个字符
    c_i = six_to_bin(byte_i)#将十六进制数转为二进制数
    c_k_i = '0'+c_i[1:]#将首字节由1变成0
    C_K = bin_to_ten(c_k_i)
    #计算原始数据的长度
    raw_length+=C_K*(128**c_index)
    c_index+=1
    start+=2
    # c_k.append(c_k_i)
    if c_i[0] == '0':
        break
#即每个字节用低7位保存c_k,除了最后一个字节的最高位为0,其余字节的最高位为1
while start<len(raw_datas):
    byte_j = raw_datas[start:start + 2]
    start+=2
    bin_byte_j = six_to_bin(byte_j)
    low_2 = bin_byte_j[-2:]
    if low_2=='00':
        L_1 = bin_to_ten(bin_byte_j[:-2])
        if L_1<=59:
            # 读取L-1个字节
            result+=raw_datas[start:start+2*(L_1+1)]
            start += 2*(L_1+1)
        else:
            #计算字节数
            # 第一个字节的高六位存储的值为60、61、62或63时
            num = L_1-59
            xiaodaun_xu = ''
            for _ in range(num):#小端序
                xiaodaun_xu = raw_datas[start:start+2]+xiaodaun_xu
                start+=2
            zijie = int(xiaodaun_xu,16)+1
            result+=raw_datas[start:start+2*zijie]
            start+=2*zijie
    elif low_2=='01':
        L = bin_to_ten(bin_byte_j[3:6])+4
        high_3 = bin_byte_j[:3]
        suihou = raw_datas[start:start+2]
        start += 2
        #将suihou转换为2进制
        suihou_bin = six_to_bin(suihou)
        O = int(high_3+suihou_bin,2)
        temp_result = huisui_yingyong(O, L, result)
        result+=temp_result
    else:#low_2=10
        L = bin_to_ten(bin_byte_j[:-2])+1
        # 读取四个字节
        O = int(raw_datas[start+2:start+4]+raw_datas[start:start+2],16)
        temp_result = huisui_yingyong(O,L,result)
        result+=temp_result
        start+=4
for i in range(len(result)):
    if i%16==0 and i!=0:
        print()
    print(result[i],end='')

2146a53b5d434ed1b13b770e8804a6d3.png

  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值