蓝桥杯训练4
复数乘法
复数可以写成 (A+Bi) 的常规形式,其中 A 是实部,B 是虚部,i 是虚数单位,满足 i2=−1;也可以写成极坐标下的指数形式 (R×e(Pi)),其中 R 是复数模,P 是辐角,i 是虚数单位,其等价于三角形式 R(cos§+isin§)。
现给定两个复数的 R 和 P,要求输出两数乘积的常规形式。
输入格式:
输入在一行中依次给出两个复数的 R1, P1,R2,P2,数字间以空格分隔。
输出格式:
在一行中按照 A+Bi 的格式输出两数乘积的常规形式,实部和虚部均保留 2 位小数。注意:如果 B 是负数,则应该写成 A-|B|i 的形式。
输入样例:
2.3 3.5 5.2 0.4
输出样例:
-8.68-8.23i
解题思路:
Alice:看起来挺简单的。
Bob: 看通过率就知道这题有坑。
Alice: 哪有坑啊?
Bob: 没看出来,先试试吧。
Alice: 有坑,后两个测试点过不去。
Bob: 看下别人怎么做的?
Alice: 你就不能自己想出来吗?
Bob: 想出来,我不想出来。╭(╯^╰)╮
Alice: 我知道了,截断?浮点数的误差?-0.00?
Bob: (^U^)ノ~YO不错不错,应该是最后一个。保留两位小数其实是去掉了两位小数位后面的位,也就是截断。这样就可能出现问题,比如说-0.001截断之后变成了-0.00,这时候应该把符号去掉的。
Alice: 还有这样的题目啊》》
Bob:看起来不像是光明正大的考察,其实是难度增加了。明天要是晴天就好了~
顺便复习一下复数的运算法则
- a+bi 和 c+di
- 加法:(a+c) + (b+d)i
- 减法:(a-c) + (b-d)i
- 乘法:(ac-bd) + (bc+ad)i
- 除法:(ac+bd)/(cc+dd) + (bc+ad)/(cc+dd)i
易错点:
- 保留两位小数,意味着“截断”,截断就有可能出现 - 0.00的情况。如
"{:.2f}+{:.2f}i".format(-0.001, 1251314)"
大C的AC
#include <stdio.h>
int main(){
double r1,p1,r2,p2;
double a, b;
scanf("%lf %lf %lf %lf", &r1, &p1, &r2, &p2);
a = r1 * r2 * cos(p1 + p2);
b = r1 * r2 * sin(p1 + p2);
if(a < 0 && a > -0.005) a = 0;
if(b < 0 && b > -0.005) b = 0;
printf("%.2lf%+.2lfi", a, b);
return 0;
}
Java的AC
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double r1 = sc.nextDouble();
double p1 = sc.nextDouble();
double r2 = sc.nextDouble();
double p2 = sc.nextDouble();
double a = r1 * r2 * Math.cos(p1+p2);
double b = r1 * r2 * Math.sin(p1+p2);
if (a < 0 && a > -0.005) {
a = 0;
}
if (b < 0 && b > -0.005) {
b = 0;
}
System.out.printf("%.2f%+.2fi\n", a, b);//%+.2f是输出保留两位小数的浮点数,且必须输出符号
}
}
python的AC
import math
# 需要使用math计算三角函数
def main():
R1, P1, R2, P2 = (float(x) for x in input().split())
# 接收输入的两个复试,其中R 和 P分别是复数模和幅角
# 我们使用复数的指数形式来计算两个复数的乘法。即(R1 * exp(P1) ) * (R2 * exp(P2) ) == (R1 * R2 * exp(P1 + P2) )
A, B = to_ab(R1 * R2, P1 + P2)
# 然后我们把复数的指数形式转换成普通形式,其中A = R * cos(P) , B = R * sin(P)。即通过复数的三角形式完成转换。
if abs(A) < 0.01:
# 由于结果要求保留两位小数,两位小数后面的位将被截断。
# 如 - 0.001将变成-0.00,这是不满足要求的,所以这里直接置零比较好。
A = 0
if abs(B) < 0.01:
# 同上
B = 0
if B < 0:
# 按照要求,保留两位小数,如果 B<0 输出A-|B|i
answer = "{:.2f}-{:.2f}i".format(A, abs(B))
else:
answer = "{:.2f}+{:.2f}i".format(A, B)
print(answer)
def to_ab(R, P):
"""通过复数的三角形式将复数从指数形式变成普通形式。输入是指数形式中的 复数模
和 幅角,输出为 复数的实部和虚部。"""
return R * math.cos(P), R * math.sin(P)
if __name__ == '__main__':
main()
别人家的AC
import math
a = [float(i) for i in input().split()]
x1 = a[0]*math.cos(a[1])
x2 = a[0]*math.sin(a[1])
y1 = a[2]*math.cos(a[3])
y2 = a[2]*math.sin(a[3])
z1 = x1*y1-x2*y2
z2 = x1*y2+x2*y1
if z1+0.005>=0 and z1<0:
z1 = '0.00'
else:
z1 = '%.2f'%z1
if z2+0.005>=0 and z2<0:
z2 = '+0.00i'
elif z2>=0:
z2 = '+%.2fi'%z2
else:
z2 = '%.2fi'%z2
print(z1+z2)
总结:
数列的片段和
给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段。例如,给定数列 { 0.1, 0.2, 0.3, 0.4 },我们有 (0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3) (0.2, 0.3, 0.4) (0.3) (0.3, 0.4) (0.4) 这 10 个片段。
给定正整数数列,求出全部片段包含的所有的数之和。如本例中 10 个片段总和是 0.1 + 0.3 + 0.6 + 1.0 + 0.2 + 0.5 + 0.9 + 0.3 + 0.7 + 0.4 = 5.0。
输入格式:
输入第一行给出一个不超过 10^5的正整数 N,表示数列中数的个数,第二行给出 N 个不超过 1.0 的正数,是数列中的数,其间以空格分隔。
输出格式:
在一行中输出该序列所有片段包含的数之和,精确到小数点后 2 位。
输入样例:
4
0.1 0.2 0.3 0.4
输出样例:
5.00
思路
这个题目很有意思,其实是一个找规律的题目,看上去很复杂,实际上如果动手写一写就可以展开发现规律。
就拿案例来举例子:
- 对0.1来说,0.1需要加的次数如下:
- 0.1,(0.1,0.2),(0.1,0.2,0.3),(0.1,0.2,0.3,0.4),一共4次。
- 对于0.2来说,0.2需要相加的次数如下:
- 0.2,(0.1,0.2),(0.2,0.3),(0.1,0.2,0.3),(0.2,0.3,0.4),(0.1,0.2,0.3,0.4),一共6次。
- 发现了没有什么规律?
每个数出现的次数与它所在的位置是相关的!因为0.1左边没有数字,所以0.1只能放在开头,因此只有4种组合,同理,0.4右边没有数字,所以只能放在结尾,也是4种组合。
因此我们可以根据数字所在的位置列出通式:
出现次数为(i + 1) * (n - i)。
但((i + 1) * (n - i))在运算过程中会发生溢出。就做了更改:
先让arr 和 (i + 1)相乘,转化为double,再乘以(n - i)就可以避免溢出的问题了。
因此求和直接用一条语句即可:
sum = sum + ((i + 1) * arr * (n - i));
大C的AC
#include <stdio.h>
int main(){
int n,i;
scanf("%d",&n);
double arr,sum;
for(i=0;i<n;i++){
scanf("%lf",&arr);
sum=sum+(arr*(n-i)*(i+1));
}
printf("%.2lf",sum);
return 0;
}
Java的伪AC
//有两个测试点超时了
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
@SuppressWarnings("resource")
Scanner in = new Scanner(System.in);
int n=in.nextInt();
double sum=0,arr;
for(int i=0;i<n;i++) {
arr=in.nextDouble();
sum=sum+(arr*(n-i)*(i+1));
}
System.out.printf("%.2f",sum);
}
}
python的AC
n = int(input())
m = [float(i) for i in input().split()]
rst = 0
for i in range(n):
rst += m[i] * (n - i) * (i + 1)
print('{:.2f}'.format(rst))
整数转罗马数字
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
- I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
- X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
- C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
示例 1:
输入: 3
输出: "III"
示例 2:
输入: 4
输出: "IV"
示例 3:
输入: 9
输出: "IX"
示例 4:
输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
示例 5:
输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.
大C的初代
#include <stdio.h>
int main(){
int arr[13]={1000,900,500,400,100,90,50,40,10,9,5,4,1};
char *str[13]={"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
int i,num;
scanf("%d",&num);
for(i=0;i<13;i++){
while(arr[i]<=num){
num=num-arr[i];
printf("%s",str[i]);
}
}
printf("\n");
return 0;
}
大C的二代
char * intToRoman(int num){
char * prm[] = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
int doc[] = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
char * sam = (char *)malloc(sizeof(char)* 20);//生成一个动态字符数组20个够了
int n = num;
sam[0] = '\0';
for(int i = 0; i < 13; i++) {
while (n >= doc[i]) {
n-=doc[i];
strcat(sam,prm[i]);//把对应的罗马数字加入字符串中
}
}
return sam;
}
JAVA的
class Solution {
public String intToRoman(int num) {
int arr[]=new int[]{1000,900,500,400,100,90,50,40,10,9,5,4,1};
String strs[]=new String[]{"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
String str="";
//从最大的开始算。
for (int i = 0; i < 13; i++) {// >=的意思就是使用多个arr[i],比如3999,要有3个1000。
while(num>=arr[i]){
num = num - arr[i];
str = str + strs[i];
}
if (num == 0) break;
}
return str;
}
}
python的
class Solution:
def intToRoman(self, num):
number = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
luoma = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']
shuru = ''
for i in range(len(number)):
while num >= number[i]:
num = num - number[i]
shuru = shuru + luoma[i]
return shuru
罗马数字转整数
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
- I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
- X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
- C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
示例 1:
输入: "III"
输出: 3
示例 2:
输入: "IV"
输出: 4
示例 3:
输入: "IX"
输出: 9
示例 4:
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
思路
- 定义一个双字母在前,单字母在后的String型数组,定义与之对应数字的数组,遍历s,先判断是否是双字母所代表的数字,再判断单字母所代表数字,将对应数字相加即可;
JAVA的
class Solution {
public int romanToInt(String s) {
String[] roman = { "IV", "IX", "XL", "XC", "CD", "CM", "I", "V", "X", "L", "C", "D", "M" };
int[] nums = { 4, 9, 40, 90, 400, 900, 1, 5, 10, 50, 100, 500, 1000 };
int num = 0;
while (s.length() > 0) {
for (int i = 0; i < roman.length; i++) {
if (s.startsWith(roman[i])) {
num += nums[i];
s = s.substring(roman[i].length());
break;
}
}
}
return num;
}
}
Python的
class Solution:
def romanToInt(self, s: str) -> int:
d= {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
ans = 0
for i in range(len(s)):
if i < len(s) - 1 and d[s[i]] < d[s[i+1]]:
ans -= d[s[i]]
else:
ans += d[s[i]]
return ans