P1572 计算分数

记录65

#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b) {
    a=abs(a);  
    b=abs(b);   
    while(b){
        int t=b;
        b=a%b;
        a=t;
    }
    return a;
}
int main(){
	int a1=0,a2=1,b1,b2,cnt=0;
	string s,st;
	cin>>s;
	bool f=0;
	for(int i=0;i<s.size();i++){
		int num=i;
		while(s[i]!='/') i++;
		st=s.substr(num,i-num);
		b1=stoi(st);
		num=++i;
		while(s[i]>='0'&&s[i]<='9') i++;
		st=s.substr(num,i-num);
		b2=stoi(st);
		i--;
		a1=a1*b2+b1*a2;
		a2=a2*b2;
		int t=gcd(a1,a2);
		a1/=t;
		a2/=t;
	}
	if(a2==1) cout<<a1;
	else cout<<a1<<'/'<<a2;
	return 0;
}

题目传送门https://www.luogu.com.cn/problem/P1572


突破口

分数计算题,最后的计算结果(用为整数则用整数表示,否则用最简分数表示)

保证,直接从前往后直接计算分数的和或者差,然后立刻化简,这么做的中间结果不会超过 int 的范围。


思路

  1. 要进行约分  👉  gcd函数
  2. 模拟分数的通分运算
  3. 每次运算后将结果化简

代码简析

#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b) {
    a=abs(a);  
    b=abs(b);   
    while(b){
        int t=b;
        b=a%b;
        a=t;
    }
    return a;
}
int main(){
	int a1=0,a2=1,b1,b2,cnt=0;
	string s,st;
	cin>>s;
	bool f=0;
	for(int i=0;i<s.size();i++){
		int num=i;
		while(s[i]!='/') i++;
		st=s.substr(num,i-num);
		b1=stoi(st);
		num=++i;
		...
	}
    ...
	return 0;
}

int gcd(int a, int b) {}  👉  计算最大公约数

首先明确:最大公约数(GCD)的数学定义是「两个整数的最大正整数公约数」,因此结果必须是非负的,所以用abs函数转换两个为正数

int a1=0(分子a),a2=1(分母a),b1(分子b),b2(分母b),cnt=0(没用变量,忘删了);

输入一个分数然后加上前一个分数,所以初始化前一个分数的分母必须为1,分子为0

string s(分数式子),st(切出来的数字);

bool f=0(没用2号,也忘删了);

int num=i;  👉  记录数字开始下标,此处为分子

while(s[i]!='/') i++;  👉 没遇到/,代表还在找数字

st=s.substr(num,i-num);  👉  切出来数字了

注意:此时i指向的是/的位置,所以i-num就是数字的长度

b1=stoi(st);  👉  字符串转数字,得到分子

注意:关于stoi函数,放在补充部分来介绍

num=++i;  👉 存分母开始的下标

#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b) {
    a=abs(a);  
    b=abs(b);   
    while(b){
        int t=b;
        b=a%b;
        a=t;
    }
    return a;
}
int main(){
	int a1=0,a2=1,b1,b2;
	string s,st;
	cin>>s;
	for(int i=0;i<s.size();i++){
		int num=i;
		while(s[i]!='/') i++;
		st=s.substr(num,i-num);
		b1=stoi(st);
		num=++i;
		while(s[i]>='0'&&s[i]<='9') i++;
		st=s.substr(num,i-num);
		b2=stoi(st);
		i--;
		a1=a1*b2+b1*a2;
		a2=a2*b2;
		int t=gcd(a1,a2);
		a1/=t;
		a2/=t;
	}
	if(a2==1) cout<<a1;
	else cout<<a1<<'/'<<a2;
	return 0;
}

while(s[i]>='0'&&s[i]<='9') i++;  👉  得到数字后的位置

st=s.substr(num,i-num);  👉  切片得到分母的数字

b2=stoi(st);  👉  分母转换为整数类型

i--;  👉  回到分母数字的尾巴的下标,因为进入下一次for循环会自动移动i,然后遇到下一个分子

a1=a1*b2+b1*a2;  👉  分子进行计算

a2=a2*b2;  👉  分母进行运算

int t=gcd(a1,a2);a1/=t;a2/=t;   👉  约分存入a分数,后面得到b分数加它

if(a2==1) cout<<a1;  👉  分母是1,直接输出整数
else cout<<a1<<'/'<<a2;  👉  输出最简分数结果


补充

在 CSP 比赛中 stoi 函数的可用性,以及 stoll 函数的相关使用规则

一、stoi 函数在 CSP 比赛中的应用与负数转换

1. CSP 比赛中是否可用

可以使用。CSP(计算机软件能力认证)的编译环境通常采用 GCC 编译器,且默认支持 C++11 及以上标准,而stoi是 C++11 引入的标准库函数(定义在<string>头文件中),因此在 CSP 比赛中使用stoi是完全合规且被支持的。

2. 能否转换负数

可以转换负数stoi能够正确解析以负号-开头的数字字符串(如"-123"),也支持解析以正号+开头的字符串(如"+456"),最终转换为对应的 int 类型整数。

补充:stoi 的注意事项
  • 数值范围:stoi的转换结果受 int 类型范围限制(通常为 −231 ~ 231−1,即 - 2147483648 ~ 2147483647),超出范围会抛出out_of_range异常;
  • 输入要求:字符串需为合法数字格式(仅允许开头的正负号 + 数字),若包含非数字字符(如"12a3"),会抛出invalid_argument异常;
  • 头文件:使用时必须包含<string>头文件。

二、stoll 函数的相关说明

1. CSP 比赛中是否可用

可以使用stoll同样是 C++11 标准库函数(定义在<string>头文件中),与stoi一样被 CSP 比赛的编译环境支持。

2. 核心特性
  • 功能:将字符串转换为long long类型的整数;
  • 负数支持:支持转换负数,规则与 stoi 一致(可解析"-1234567890123"这类负数字符串);
  • 数值范围:long long的范围为 −263 ~ 263−1(即 - 9223372036854775808 ~ 9223372036854775807),远大于 int,适合处理 CSP 中常见的大数场景;
  • 其他规则:输入格式要求、异常类型与 stoi 完全一致。

三、示例代码

#include <iostream>
#include <string>
using namespace std;

int main() {
    // stoi转换负数、正数
    string s1 = "-12345";
    string s2 = "+67890";
    int num1 = stoi(s1);
    int num2 = stoi(s2);
    cout << "stoi转换结果:" << num1 << " " << num2 << endl; // 输出:-12345 67890

    // stoll转换大数负数
    string s3 = "-1234567890123456789";
    long long num3 = stoll(s3);
    cout << "stoll转换结果:" << num3 << endl; // 输出:-1234567890123456789

    return 0;
}

总结

  1. stoistoll可在 CSP 比赛中使用(C++11 标准支持),且都能正确转换负数
  2. stoi适用于 int 范围的整数转换,stoll适用于 long long 范围的大数转换;
  3. 使用时需包含<string>头文件,确保输入字符串为合法数字格式,避免超出类型范围。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值