CCF CSP认证 20230902 坐标变换(其二)暴力题解+满分题解 Python实现

概述

  本题目80分容易,满分的关键在于要把时间复杂度从O(m*n)降为O(m+n)。题目版权归CCF所有,真题跳转官网查看
  真题来源:坐标变换(其二)
  官网地址:www.cspro.org(模拟考试入口)

题目分析

  对 m 个坐标 (x,y) 在给定含拉伸和旋转n 个操作中,选择从第 i 步操作到第 j 步操作完成 j-i+1 个操作,其中 1 <= i <= j <= n ,并输出最终结果。
  在细心审题后发现:1、所有操作,都是基于初始坐标 (x,y) 进行操作的。2、拉伸和旋转两种操作互不影响

方法一:暴力题解

  • 接受n个操作,将其转为列表形式并存储在操作列表 n_line
  • 接受m个坐标的操作范围及其坐标,分别对应到i, j, x, y
  • 对操作列表按照操作范围进行切片得到操作集 n_line[i-1:j]
  • 遍历操作集,得到最终的拉伸系数k旋转弧度theta
  • 记录拉伸后的坐标为 (x0,y0) ,根据旋转公式:
    x = x0 · cos(theta) - y0 · sin(theta)
    y = x0 · sin(theta) + y0 · cos(theta)
  • 得到最终坐标后,格式化输出。
具体实现
from math import sin, cos
n, m = map(int, input().split())
n_line = []
for t in range(n):
    n_line.append(list(map(float, input().split())))	# 操作列表 eg:[[1.0,0.5],[2.0,5.5]]
for t in range(m):
    i, j, x, y = map(int, input().split())	# 操作范围及初始坐标
    lines = n_line[i-1:j]	# 操作集
    theta = 0	# 初始弧度
    for line in lines:
        if line[0] == 1:
            k = line[1]
            x *= k
            y *= k
        elif line[0] == 2:
            theta += line[1]
    x0 = x		# 拉伸后的x坐标
    y0 = y		# 拉伸后的y坐标
    x = x0*cos(theta) - y0*sin(theta)	# 最终x
    y = x0*sin(theta) + y0*cos(theta)	# 最终y
    lines.clear()	# 释放操作集
    print("{} {}".format(x, y))
提交结果

20230902暴力题解

方法二:满分题解

  在具体叙述之前,先了解一下降低时间复杂度的方法有哪些。

降低复杂度的方法

  将时间复杂度从O(m * n)降低到O(m + n)的办法通常涉及到改变算法的策略,以便更有效地利用输入数据,可能的策略如下:

  • 使用更有效的算法:有些算法在处理特定问题时比其他算法更有效。例如,如果你正在对两个列表进行搜索,使用哈希表(HashMap)而不是线性搜索可以大大提高效率。
  • 预处理:在某些情况下,你可以在运行主要算法之前对输入进行预处理。例如,如果你正在对一个列表进行排序,并需要在随后的搜索中使用这个列表,那么预先对列表进行排序可能会使搜索更快。
  • 空间换时间:有时,你可以通过使用更多的内存来提高算法的效率。例如,如果你正在处理大量数据,并且需要多次访问这些数据,那么将数据存储在缓存或哈希表中可能会使访问速度更快。
  • 分治策略:将问题分解成更小的子问题,然后分别解决这些子问题,最后将结果合并起来。这种策略可以有效地降低时间复杂度。
  • 利用数据结构:不同的数据结构有着不同的操作复杂度。例如,如果需要频繁地查找和修改数据,那么使用哈希表可能比使用数组更有效。
  • 懒加载:只在需要时才进行计算或处理,而不是一开始就处理所有的数据。这种方法可以节省计算资源并降低时间复杂度。
  • 并行化:如果算法可以并行运行,那么你可以利用现代计算机的多核性能来降低时间复杂度。
题解详述
  • 由于拉伸和旋转两种操作互不影响。可结合分治的思想,分别对拉伸、旋转操作进行各自的叠加处理
  • 利用两个列表,分别记录拉伸系数k、弧度theta在每次操作叠加后的值:拉伸系数的变化是累积弧度的变化是累加。所以,可记录初始拉伸系数为1初始弧度为0
  • 接受 n 个操作。当接受的操作为拉伸,则将上个操作累积的拉伸系数 ✖️ 本次操作的拉伸系数 添加到 拉伸系数列表 k_li 中,并将上个操作累加的弧度 添加到 弧度列表 theta_li 中;
    当接受的操作为旋转,则将上个操作累加的弧度 ➕ 本次操作的弧度 添加到 弧度列表 theta_li ,并将上个操作累积的拉伸系数 添加到 拉伸系数列表 k_li 中。
  • 接受 m 个坐标的操作范围及其坐标。由于在 theta_lik_li 中分别 加入了初始值 ,对每个坐标 (x0,y0) 进行i ~ j 步操作:
    最终拉伸系数k = 第 j 步的累积拉伸系数k_li[j] ➗ 第 i-1 步的拉伸系数k_li[i-1]
    最终弧度theta = 第 j 步的累加弧度theta_li[j] ➖ 第 i-1 步的弧度theta_li[i-1]
  • 最后,计算最终坐标 (x,y) 并格式化输出。
具体实现
from math import sin, cos
n, m = map(int, input().split())
k_li = [1.0]		# 定义拉伸系数累积列表并赋初值
theta_li = [0]		# 定义弧度累加列表并赋初值
for t in range(n):
    _type, _data = map(float, input().split())		# 获取操作类型 操作值
    if _type == 1:
        k_li.append(k_li[t] * _data)	# 累积
        theta_li.append(theta_li[t])
    elif _type == 2:
        k_li.append(k_li[t])
        theta_li.append(theta_li[t] + _data)	# 累加
for t in range(m):
    i, j, x0, y0 = map(int, input().split())	# 操作范围及坐标
    k = k_li[j] / k_li[i-1]		# 计算最终拉伸系数
    theta = theta_li[j] - theta_li[i-1]		# 计算最终弧度
    x0 *= k
    y0 *= k
    x = x0*cos(theta) - y0*sin(theta)
    y = x0*sin(theta) + y0*cos(theta)
    print("%f %f" % (x, y))
提交结果

20230902满分题解

  • 21
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值