CSP认证2023-09:坐标变换(其一)、坐标变换(其二)、梯度求解,python满分代码解答

CSP认证2023-09:坐标变换(其一)、坐标变换(其二)、梯度求解,python满分代码解答

目录

一、坐标变换(其一)

问题描述

输入和输出

思路

代码和结果

c++代码

python代码

二、坐标变换(其二)

问题描述

输入和输出

思路

代码和结果

c++代码

​编辑 python代码

改进 

c++代码

python代码

三、梯度求解

问题描述

输入和输出

思路

代码和结果


一、坐标变换(其一)

问题描述

75a34c5f06f24e108691b40c9f6c76a1.png

输入和输出

26c6bf171e7b4cab82b592f9ae9b6724.png

输入

3 2
10 10
0 0
10 -20
1 -1
0 0

输出 

21 -11
20 -10

思路

定义一个起始原点坐标(0,0),将n个操作依次累加,获得移动后的坐标(x_move,y_move)。接着输入m行坐标(x_res,y_res)时,直接加上移动后的坐标(x_move,y_move)输出结果即可。

代码和结果

c++代码

#include<iostream>
using namespace std;
int main() {
	int n, m;
	cin >> n >> m;
	int move_x = 0;
	int move_y = 0;
	for (int i = 0; i < n; i++) {//m行数据
		int x,y;
		cin >> x>>y;
		move_x += x;
		move_y += y;		
	}
	for (int j = 0; j < m; j++) {
		int input_x, input_y;
		cin >> input_x >> input_y;
		cout << input_x + move_x << " " << input_y + move_y << endl;
	}
}

2197131705794cc7b25ccfc007b13abb.png

python代码

n,m=list(map(int,input().split()))
x_move ,y_move= 0,0
for i in range(n):
    x_1,y_1 = list(map(int,input().split()))
    x_move+=x_1
    y_move+=y_1
for j in range(m):
    x_res, y_res = list(map(int, input().split()))
    x_res += x_move
    y_res += y_move
    print(x_res,' ',y_res)

e763ccd9e3644fd3946f1f9c0ae42702.png

二、坐标变换(其二)

问题描述

06edc47ea35645e4a8129e62f6bb69c1.jpeg

输入和输出

aae3c87c5bd1429c9856065fed44021c.png输入

10 5
2 0.59
2 4.956
1 0.997
1 1.364
1 1.242
1 0.82
2 2.824
1 0.716
2 0.178
2 4.094
1 6 -953188 -946637
1 9 969538 848081
4 7 -114758 522223
1 9 -535079 601597
8 8 159430 -511187

输出

-1858706.758 -83259.993
-1261428.46 201113.678
-75099.123 -738950.159
-119179.897 -789457.532
114151.88 -366009.892

思路

1、首先将利用两个数组op和value将操作和其对应的值存起来

2、在输入每一个查询时,找到数组op和value的起始位置和终止位置

3、从起始位置和终止位置进行运算即可

代码和结果

这里给出c++代码和python代码

c++代码

#include<iostream>
#include<math.h>
#include<iomanip>
using namespace std;

int main(){
	cout.setf(ios::fixed);

	cout.precision(3); // 精度为输出小数点后3位

	int n, m;
	cin >> n >> m;
	static int op[100001];
	static double value[100001];
	for (int i = 1; i <= n; i++) {
		int op1;
		double value1;
		cin >> op1 >> value1;
		op[i] = op1;
		value[i] = value1;
	}
	for (int j = 0; j < m; j++) {
		int op_start, op_end;
		double value1, value2;
		cin >> op_start >> op_end >> value1 >> value2;

		for (int op_index = op_start; op_index <= op_end; op_index++) {
			//对于第op_index进行以下操作
			int operation1 = op[op_index];//进行缩放或旋转
			double v1 = value[op_index];//
			if (operation1 == 1) {
				value1 = value1 * v1;
				value2 = value2 * v1;
			}
			else {
				double value1_copy = value1;
				value1 = value1 * cos(v1) - value2 * sin(v1);
				value2 = value1_copy * sin(v1) + value2 * cos(v1);
			}

		}
		cout <<value1 << " " << value2 << endl;

	}
}

99d859afa82646348fe2f1c4409cde30.png

python代码

import math

n,m = list(map(int,input().split()))
op = [0]
value = [0.]
for i in range(1,n+1):
    operate, v = input().split()
    op.append(int(operate))
    value.append(float(v))
for i in range(m):
    op_start_s,op_end_s,value1_s,value2_s= input().split()
    op_start = int(op_start_s)
    op_end = int(op_end_s)
    value1 = float(value1_s)
    value2 = float(value2_s)
    for j in range(op_start,op_end+1):
        #对于第op_index进行以下操作
        operation1 = op[j] # 进行缩放或旋转
        v1 = value[j]
        if operation1 == 1:
            value1 = value1 * v1
            value2 = value2 * v1
        else:
            value1_copy = value1 #这一步很重要
            value1 = value1 * math.cos(v1) - value2 * math.sin(v1)
            value2 = value1_copy * math.sin(v1) + value2 * math.cos(v1)
    print(value1,' ',value2)

feed4f10e22b443aa066efbf12d49233.png

改进 

然而由于查询过程中存在重复的查询,导致时间复杂度过高而只得到了80分。

通过观察发现,题目让我们求的是给定坐标经过op_start到op_end操作之后的结果,又通过操作内容可知,只包含旋转和坐标缩放这两种操作。从中可以得到以下结论:

首先,这两种操作是不受彼此干扰的;其次,旋转和缩放是可以累积计算的

因此,我们可以通过两个数组记录从1到n个操作的结果,每一步操作是旋转操作和缩放操作的累积。首先计算从op_start到op_end旋转的角度,只需要用op_end旋转的角度减去op_start的角度;其次计算从op_start到op_end缩放的倍数,只需要用op_end缩放的倍数除以op_start缩放的倍数。

c++代码

//存在的问题:考虑到不能每次去遍历操作,因为这样会导致复杂度很高
//解决办法:将操作数组记录下来,因为缩放和旋转是无关的,所以可以使用两个数组存放;旋转是循环的可以实现累加,缩放也是可以累乘的
#include<iostream>
#include<math.h>
#include<iomanip>
using namespace std;
#define pai 3.14159265358979323846
int main() {
	cout.setf(ios::fixed);

	cout.precision(3); // 精度为输出小数点后3位

	int n, m;
	cin >> n >> m;
	//static int op_scale[100001];//分开存放两个操作
	//static int op_xuanzhuan[100001];
	static double value_scale[100001]={1};
	static double value_xuanzhuan[100001]={0};

	//累积缩放值
	double scale = 1;
	//累积旋转值,需要mod 2pai
	double xuanzhuan = 0;
	for (int i = 1; i <= n; i++) {//现在存放的就不是第i个操作了,而是前i个操作的累积
		int op1;
		double value1;
		cin >> op1 >> value1;
		if (op1 == 1) {
			//同时记录到上一轮的旋转
			value_xuanzhuan[i] = xuanzhuan;
			scale = scale * value1;
			value_scale[i] = scale;
		}
		else {
			//同时记录到上一轮的缩放
			value_scale[i] = scale;
			xuanzhuan = xuanzhuan + value1;//先不用取余,最后结果取余就行
			value_xuanzhuan[i] = xuanzhuan;
		}
	}
	for (int j = 0; j < m; j++) {
		int op_start, op_end;
		double value1, value2;
		cin >> op_start >> op_end >> value1 >> value2;

		//看op_start的缩放是多少,op_end的缩放系数是多少,除一下就是缩放的系数了
		double op_scale_start = value_scale[op_start-1];
		double op_scale_end = value_scale[op_end];

		double op_xuanzhuan_start = value_xuanzhuan[op_start-1];
		double op_xuanzhuan_end = value_xuanzhuan[op_end];

		//首先计算缩放了多少
		double scale_start_end = op_scale_end / op_scale_start;
		double xuanzhuan_start_end = fmod((op_xuanzhuan_end - op_xuanzhuan_start), 2 * pai);//op_xuanzhuan_start操作也是进行的
		double value1_copy = value1;
		value1 = scale_start_end*(value1 * cos(xuanzhuan_start_end) - value2 * sin(xuanzhuan_start_end));
		value2 = scale_start_end*(value1_copy * sin(xuanzhuan_start_end) + value2 * cos(xuanzhuan_start_end));
		cout << value1 << " " << value2 << endl;
	}
	return 0;

}

a0bbe917e0bc418a89cd2fa9466b7007.png

python代码

import math
n,m = list(map(int,input().split()))
#累积缩放值
scale = 1
#累积旋转值,需要mod 2pai
xuanzhuan = 0
value_scale = [1]
value_xuanzhuan = [0]
for i in range(1,n+1):#现在存放的就不是第i个操作了,而是前i个操作的累积
    op1_,value1_ = input().split()
    op1 = int(op1_)
    value1 = float(value1_)

    if op1 == 1:
        #同时记录到上一轮的旋转
        value_xuanzhuan.append(xuanzhuan)
        scale = scale * value1
        value_scale.append(scale)

    else:
        #同时记录到上一轮的缩放
        value_scale.append(scale)
        xuanzhuan = xuanzhuan + value1#先不用取余,最后结果取余就行
        value_xuanzhuan.append(xuanzhuan)
for j in range(m):
    op_start_s, op_end_s, value1_s, value2_s = input().split()
    op_start = int(op_start_s)
    op_end = int(op_end_s)
    value1 = float(value1_s)
    value2 = float(value2_s)

    #看op_start的缩放是多少,op_end的缩放系数是多少,除一下就是缩放的系数了
    op_scale_start = value_scale[op_start-1]
    op_scale_end = value_scale[op_end]

    op_xuanzhuan_start = value_xuanzhuan[op_start-1]
    op_xuanzhuan_end = value_xuanzhuan[op_end]

    #首先计算缩放了多少
    scale_start_end = op_scale_end / op_scale_start
    xuanzhuan_start_end = (op_xuanzhuan_end - op_xuanzhuan_start)%(2 * math.pi)#op_xuanzhuan_start操作也是进行的
    value1_copy = value1
    value1 = scale_start_end*(value1 * math.cos(xuanzhuan_start_end) - value2 * math.sin(xuanzhuan_start_end))
    value2 = scale_start_end*(value1_copy * math.sin(xuanzhuan_start_end) + value2 * math.cos(xuanzhuan_start_end))
    print(value1, ' ', value2)

三、梯度求解

问题描述

5f0b29a6f0c0470389daa0a260640276.png

输入和输出

5560175517f045d79e2c2b02ea1b7b2b.png

输入

2 2
x1 x1 x1 * x2 + *
1 2 3
2 3 4

输出

15
3

思路

本题是一道求偏导的题,计算过程很清晰,但是实现起来还是比较麻烦的。接下来简要介绍一下代码的思路:

1、首先,对于每一次输入,将每一个自变量的值存储起来,注:将逆波兰式的多项式使用copy()进行复制,防止后续操作修改了其中的内容。

for _ in range(m):
    data = list(map(int,input().split()))
    bianliang_j = data[0]
    poly_copy_list = poly_list.copy()
    value_list = data[1:]#从自变量1到自变量n的值
    bianliang_value_dict = {}#变量值字典
    for i in range(len(value_list)):
        if i+1!=int(bianliang_j):
            x_i = 'x'+str(i+1)
            bianliang_value_dict[x_i] = str(value_list[i])
        else:
            bianliang_j_value = int(value_list[i])

2、将多项式中的不要求偏导的自变量替换成其值,这样就仅包含一个自变量,可以使用字典存储多项式。

#复制多项式,将多项式中的不要求偏导的自变量替换成其值
for i in range(len(poly_copy_list)):
    if poly_copy_list[i] in bianliang_value_dict.keys():
        poly_copy_list[i]=bianliang_value_dict[poly_copy_list[i]]
class mul_poly():#对于一个仅包含一个变量的多项式,仅存储其系数和次数即可,但是考虑到稀疏性,得用字典来存储
    def __int__(self):
        self.poly = {}#键表示的是多项式的次数,值表示系数

3、开始处理偏导,因为逆波兰式可能只有一项(数值或变量),需要判断一下。如果只有一项,直接用字典形式表示多项式;否则利用栈读取逆波兰式。

  • 在处理逆波兰式时,为了简化运算符的判断,将减法用加法、乘法、系数-1进行表示。
  • 字典形式的多项式的计算方法为: 在乘法中,两重循环多项式a和多项式b的次数项(键),进行次数(键)相加,系数(值)相乘的操作;多项式加法为,单次循环多项式b的次数(键),如果多项式a中没有,则将多项式b的键值对添加到多项式a中;否则将多项式b的系数(值)和多项式a的系数(值)作和,即可完成加法运算。
 if len(poly_copy_list)==1:#只含一个数值或变量
        temp = trans_mul_poly(poly_copy_list[0]).poly
    else:
        temp = piandao(poly_copy_list)
def piandao(poly):
    '''
    :param
        -poly: 列表形式的多项式
    :return:
    '''
    new_poly = []
    for ele in poly:#a-b = a+ (-1 *)b 减法运算变成加法和乘法
        if ele=='-':#处理减法运算
            new_poly.append('-1')
            new_poly.append('*')
            new_poly.append('+')
        else:
            new_poly.append(ele)
    poly_stack =[]
    # #创建一个多项式字典,用以记录多项式加法和乘法的计算结果
    #通过逆波兰式读取结果
    for ele in new_poly:
        if ele not in ['+','*']:#如果不是运算符,则加入poly_stack中
            poly_stack.append(ele)
        else:
            b = poly_stack.pop()
            a = poly_stack.pop()
            #a和b初始只可能是 数值(可正可负)、自变量x_1 、之后存在(x_1的多项式)
            b_mul_poly = trans_mul_poly(b)
            a_mul_poly = trans_mul_poly(a)#转换成mul_poly对象
            res = compute(a_mul_poly,b_mul_poly,ele)
            poly_stack.append(res)
    result = poly_stack.pop()
    return result.poly
def compute(a_mul_poly,b_mul_poly,ele):
    '''
    :param a_mul_poly:
    :param b_mul_poly:
    :param ele: 运算符,取值为"+"或"*"
    :return:
    '''
    if ele=='*':
        res = mul_poly()
        res.poly = {}
       #对应位置的系数相乘
        for a in a_mul_poly.poly.keys():
            for b in b_mul_poly.poly.keys():
                # 次数相加,系数相乘
                a_b_sum_cishu = a+b
                a_b_sum_xishu = a_mul_poly.poly[a]*b_mul_poly.poly[b]
                if a_b_sum_cishu not in res.poly.keys():
                    res.poly[a_b_sum_cishu] = a_b_sum_xishu
                else:
                    res.poly[a_b_sum_cishu] += a_b_sum_xishu
        return res
    else:
        # 对应位置的系数相加
        for b_cishu in b_mul_poly.poly.keys():
            if b_cishu not in a_mul_poly.poly.keys():
                a_mul_poly.poly[b_cishu] = b_mul_poly.poly[b_cishu]
            else:
                a_mul_poly.poly[b_cishu] += b_mul_poly.poly[b_cishu]
        return a_mul_poly

4、获得结果

res = 0
for key in temp.keys():#键为多项式次数,值为系数
    if key == 0:#常数项求偏导为0
        continue
    elif key == 1: #存在0的0次方
        res+=temp[key]
    else:
        res+=key*temp[key]*bianliang_j_value**(key-1)
print(int(res%(10**9+7)))

代码和结果

class mul_poly():#对于一个仅包含一个变量的多项式,仅存储其系数和次数即可,但是考虑到稀疏性,得用字典来存储
    def __int__(self):
        self.poly = {}#键表示的是多项式的次数,值表示系数
def compute(a_mul_poly,b_mul_poly,ele):
    '''
    :param a_mul_poly:
    :param b_mul_poly:
    :param ele: 运算符,取值为"+"或"*"
    :return:
    '''
    if ele=='*':
        res = mul_poly()
        res.poly = {}
       #对应位置的系数相乘
        for a in a_mul_poly.poly.keys():
            for b in b_mul_poly.poly.keys():
                # 次数相加,系数相乘
                a_b_sum_cishu = a+b
                a_b_sum_xishu = a_mul_poly.poly[a]*b_mul_poly.poly[b]
                if a_b_sum_cishu not in res.poly.keys():
                    res.poly[a_b_sum_cishu] = a_b_sum_xishu
                else:
                    res.poly[a_b_sum_cishu] += a_b_sum_xishu
        return res
    else:
        # 对应位置的系数相加
        for b_cishu in b_mul_poly.poly.keys():
            if b_cishu not in a_mul_poly.poly.keys():
                a_mul_poly.poly[b_cishu] = b_mul_poly.poly[b_cishu]
            else:
                a_mul_poly.poly[b_cishu] += b_mul_poly.poly[b_cishu]
        return a_mul_poly
def trans_mul_poly(data):#将数据转成mul_poly对象
    if isinstance(data,mul_poly):
        return data
    else:
        res = mul_poly()
        res.poly = {}
        if 'x' in data:#是x自变量
            res.poly[1] = 1
        else:#是数值
            res.poly[0]=int(float(data)%(10**9+7))
        return res

def piandao(poly):
    '''
    :param
        -poly: 列表形式的多项式
    :return:
    '''
    new_poly = []
    for ele in poly:#a-b = a+ (-1 *)b 减法运算变成加法和乘法
        if ele=='-':#处理减法运算
            new_poly.append('-1')
            new_poly.append('*')
            new_poly.append('+')
        else:
            new_poly.append(ele)
    poly_stack =[]
    # #创建一个多项式字典,用以记录多项式加法和乘法的计算结果
    #通过逆波兰式读取结果
    for ele in new_poly:
        if ele not in ['+','*']:#如果不是运算符,则加入poly_stack中
            poly_stack.append(ele)
        else:
            b = poly_stack.pop()
            a = poly_stack.pop()
            #a和b初始只可能是 数值(可正可负)、自变量x_1 、之后存在(x_1的多项式)
            b_mul_poly = trans_mul_poly(b)
            a_mul_poly = trans_mul_poly(a)#转换成mul_poly对象
            res = compute(a_mul_poly,b_mul_poly,ele)
            poly_stack.append(res)
    result = poly_stack.pop()
    return result.poly
n,m=list(map(int,input().split()))
poly_list = input().split()
for _ in range(m):
    data = list(map(int,input().split()))
    bianliang_j = data[0]
    poly_copy_list = poly_list.copy()
    value_list = data[1:]#从自变量1到自变量n的值
    bianliang_value_dict = {}#变量值字典
    for i in range(len(value_list)):
        if i+1!=int(bianliang_j):
            x_i = 'x'+str(i+1)
            bianliang_value_dict[x_i] = str(value_list[i])
        else:
            bianliang_j_value = int(value_list[i])
    #复制多项式,将多项式中的不要求偏导的自变量替换成其值
    for i in range(len(poly_copy_list)):
        if poly_copy_list[i] in bianliang_value_dict.keys():
            poly_copy_list[i]=bianliang_value_dict[poly_copy_list[i]]
    if len(poly_copy_list)==1:#只含一个数值或变量
        temp = trans_mul_poly(poly_copy_list[0]).poly
    else:
        temp = piandao(poly_copy_list)
    res = 0
    for key in temp.keys():#键为多项式次数,值为系数
        if key == 0:#常数项求偏导为0
            continue
        elif key == 1: #存在0的0次方
            res+=temp[key]
        else:
            res+=key*temp[key]*bianliang_j_value**(key-1)
    print(int(res%(10**9+7)))

8f2581094a034998a4f4bbd8a396b647.png

  • 33
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值