当前位置: 首页 > news >正文

[蓝桥杯]R格式(CC++双语版)

        如果你看了多篇题解还是不明白,那么相信该文可以让你有所收获,本文同样适用于不会高精度算法的同学,只是理解起来可能比会高精度的同学略难以理解。话不多说,我们开始学习!

题目链接

(这道题不建议用洛谷,洛谷少了一个一直进位直到进到最高位的测试用例)

          蓝桥杯2024年第十五届省赛真题-R 格式 - C语言网

题目理解

        简单来说,小蓝研究的这种 R 格式表示方法,就是对于一个大于 0 的浮点数(比如 3.14 这样的数),给定一个整数参数 n(比如 n = 2 ),先把这个浮点数乘以 2 的 n 次方(这里就是 3.14 乘以 2 的 2 次方,也就是 3.14×4 = 12.56 ),然后把得到的结果四舍五入到离它最近的整数(12.56 四舍五入后就是 13 ),最后得到的这个整数就是该浮点数用 R 格式表示的结果。

解题思路

        最开始的浮点型我们用字符串类型接收,原因我放在了疑难解答处。

        我们以题目示例展开讲解:

  1.存放数字

下面是浮点数在字符型数组中的存储情况:

  2.记录小数点位置并将其转为整型 

但是字符型是不便于我们进行运算的,于是我们创建整型数组将其放入并记录小数点位置:

与此同时,我们将字符型转换为整型,放入新建的intd数组中: 

  3.计算整型数乘以2ⁿ

后面我们要做的就简单了,只需要计算如下公式:

4.计算操作 

计算操作为将该数组每一位数乘2,然后处理进位,我们还以示例中n=2举例:

第一次乘2,不需要进位:

第二次乘2:

进位,当某数组中有大于等于10的数字时,我们将该数字减10,并给前一位加1。如果是数组的首位数字,我们应该在最前方添加一位数字‘1’:

        但是我们操作时候发现进位不方便操作,并且添加数字时也遇到了困难。所以从一开始,这个数组是逆序存放的,也就是说,此时存储的形式为:

         这样一来,不仅方便从后向前记录小数点位置。我们首位数字进位时,只需在末尾添加1就可以了。(至于逆序,大家完全不用纠结,我们只需要在输出的时候逆序输出就可以,学过高精度的同学应该很好理解):

//循环次数为字符串长度
//从后向前遍历,方便记录
for(int i=d.length()-1;i>=0;i--)
{
	if(d[i]!='.')
	{
		//move变量用于跟踪数组,直到找到小数点
		move++;
		//将字符型转为整型存入intd数组中
		intd.push_back(d[i]-'0');
	}
	else
	{
		//用pos记录小数点位置
		pos=move;
	}
}

        其实很简单,只需要将i的初始值赋值为字符串长度,i小于0时终止循环,我们就从数组末尾开始操作了。

        下面是我们计算的代码:

//进行n次,每一位乘2
for(int i=0;i<n;i++)
{
	//每一位乘2并进位
	for(int j=0;j<intd.size();j++)
	{
		intd[j]*=2;
	}
	for(int j=0;j<intd.size();j++)
	{
		if(intd[j]>=10)
		{
			//数组第一位从0开始,故减1
			if(j==intd.size()-1)
			{
				intd[j]-=10;
				//向数组末尾加1,即进位
				intd.push_back(1);
			}
			else
			{
				intd[j]-=10;
				intd[j+1]++;
			}
		}
	}
}

   5.处理四舍五入

        此时我们用于记录小数点后位数的pos就有用了,因为数组从0开始存储,那么此时数组中的pos-1就是小数点后第一位数。比如数字为 3.14 时,pos=2(小数点后的位数)。因为是逆序,所以整型数组为413,数组[pos-1]即第2位数字‘1’。其为小数点后第一位数字。当其大于等于5时,进位即可。我们接着刚刚的例子分析:

        数字大于等于5,我们向前加1,小数点后的数字就不用理会,输出时我们限制条件使其不进行输出即可:

        代码如下:

//处理四舍五入
//intd[pos-1]为小数点后一位的数字,大于5进位
if(intd[pos-1]>=5)
{
	//前一位进位加1
	intd[pos]++;
	//intd[pos] 加 1 后可能会产生进位,且进位可能会持续影响后续高位
	if(intd[pos]>=10)
	{
		//数组第一位从0开始,故减1
		if(pos==intd.size()-1)
		{
			intd[pos]-=10;
			//向数组末尾加1,即进位
			intd.push_back(1);
		}
		else
		{
			intd[pos]-=10;
			intd[pos+1]++;
		}
	}
}

  6.输出结果

        我们给for的循环条件限定如下,这样就可以逆序输出小数点以前的数字:

//因为数组中的数字是逆序的,这里输出需要逆序
for(int j=intd.size()-1;j>=pos;j--)
{
	cout<<intd[j];
}

完整代码 

   1.C++版

#include<bits/stdc++.h>
using namespace std;
	
int main()
{
	int n;
	//string省去了最后去除末尾0的步骤
	string d;
	cin>>n>>d;
	//记录小数点后有几位数,即小数点位置
	int pos=0,move=0;
	//创建数组intd,作为字符串d转换成可运算的int型数组的容器
	vector<int> intd;
	//循环次数为字符串长度
	//从后向前遍历,方便记录
	for(int i=d.length()-1;i>=0;i--)
	{
		if(d[i]!='.')
		{
			//move变量用于跟踪数组,直到找到小数点
			move++;
			//将字符型转为整型存入intd数组中
			intd.push_back(d[i]-'0');
		}
		else
		{
			//用pos记录小数点位置
			pos=move;
		}
	}
	//进行n次,每一位乘2
	for(int i=0;i<n;i++)
	{
		//每一位乘2并进位
		for(int j=0;j<intd.size();j++)
		{
			intd[j]*=2;
		}
		for(int j=0;j<intd.size();j++)
		{
			if(intd[j]>=10)
			{
				//数组第一位从0开始,故减1
				if(j==intd.size()-1)
				{
					intd[j]-=10;
					//向数组末尾加1,即进位
					intd.push_back(1);
				}
				else
				{
					intd[j]-=10;
					intd[j+1]++;
				}
			}
		}
	}
	//处理四舍五入
	//intd[pos-1]为小数点后一位的数字,大于5进位
	if(intd[pos-1]>=5)
	{
		//前一位进位加1
		intd[pos]++;
		//intd[pos] 加 1 后可能会产生进位,且进位可能会持续影响后续高位
		if(intd[pos]>=10)
		{
			//数组第一位从0开始,故减1
			if(pos==intd.size()-1)
			{
				intd[pos]-=10;
				//向数组末尾加1,即进位
				intd.push_back(1);
			}
			else
			{
				intd[pos]-=10;
				intd[pos+1]++;
			}
		}
	}
	//因为数组中的数字是逆序的,这里输出需要逆序
	for(int j=intd.size()-1;j>=pos;j--)
	{
		cout<<intd[j];
	}
	return 0;
}

   2.C语言版 

        思路是一样的,只是因为数组是静态的,需要进行附加操作。

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define MAX_LEN 100000  // 假设足够大的数组长度

int main() {
    int n;
    char d[MAX_LEN];
    scanf("%d %s", &n, d);  // 读取输入参数n和浮点数字符串d

    int intd[MAX_LEN * 2] = {0};  // 存储各数字位的数组,初始化为0
    int len = 0;  // 数组当前长度
    int pos = 0;  // 小数点后的位数
    bool has_decimal = false;  // 标记是否有小数点

    // 逆序处理字符串d,将每个数字存入数组,并记录小数点位置
    for (int i = strlen(d) - 1; i >= 0; i--) {
        if (d[i] != '.') {
            intd[len++] = d[i] - '0';  // 字符转数字并存入数组
        } else {
            pos = len;  // 记录小数点后的位数(当前已处理数字的个数)
            has_decimal = true;
        }
    }

    // 乘以2的n次方
    for (int i = 0; i < n; i++) {
        // 每位数字乘以2
        for (int j = 0; j < len; j++) {
            intd[j] *= 2;
        }
        // 处理进位
        for (int j = 0; j < len; j++) {
            if (intd[j] >= 10) {
                int carry = intd[j] / 10;
                intd[j] %= 10;
                if (j + 1 < len) {
                    intd[j + 1] += carry;  // 向前进位
                } else {
                    intd[len++] = carry;  // 扩展数组
                }
            }
        }
    }

    // 四舍五入处理
    if (has_decimal && pos > 0) {  // 当存在小数部分时才处理
        if (intd[pos - 1] >= 5) {
            // 处理进位,需要考虑pos可能等于len的情况
            int j = pos;
            bool carry = true;  // 初始需要进位
            while (carry && j < MAX_LEN * 2) {
                if (j >= len) {
                    intd[len++] = 1;  // 扩展数组
                    carry = false;
                } else {
                    intd[j] += 1;
                    if (intd[j] < 10) {
                        carry = false;
                    } else {
                        intd[j] = 0;
                        j++;  // 继续进位
                    }
                }
            }
        }
    }

    // 输出整数部分(逆序输出小数点前的部分)
    // 需要处理全零的情况
    bool all_zero = true;
    for (int j = len - 1; j >= pos; j--) {
        if (intd[j] != 0) {
            all_zero = false;
            break;
        }
    }
    
    if (all_zero && len > pos) {
        printf("0");  // 如果整数部分全零,输出0
    } else {
        for (int j = len - 1; j >= pos; j--) {
            printf("%d", intd[j]);
        }
    }
    printf("\n");

    return 0;
}

疑难解答

     1.用字符串接收浮点数的原因

精度问题

  • 浮点型存储限制float 和 double存在精度限制。对于一些小数,无法精确表示其真实值,会产生舍入误差。例如,对于一些无限循环小数或者非常大 / 小的数,浮点型变量只能存储其近似值。
  • 题目要求精确计算:本题需要将浮点数乘以 2^n 后再进行四舍五入,在这个过程中,如果使用浮点型进行计算,精度误差可能会不断累积,最终导致结果不准确。而使用字符串存储浮点数,可以将其每一位数字单独存储和处理,避免了精度丢失问题,保证计算结果的准确性。

大数值处理

  • 范围限制:浮点型有其表示范围的限制,当 n 较大时,浮点数乘以 2^n 可能会超出浮点型所能表示的范围,导致溢出错误。
  • 字符串灵活性:使用字符串可以处理任意长度的数字,不受数据类型范围的限制。程序可以按位处理字符串中的数字,无论数字有多大,都能正确进行乘法和进位操作。

记录小数点

  • 乘法和进位处理:本题的核心是将浮点数乘以 2^n 并处理进位,使用字符串可以方便地按位进行乘法和进位操作。可以将字符串中的每一位数字转换为整数,乘以 2 后处理进位,再将结果存储回字符串中。
  • 四舍五入操作:在进行四舍五入时,通过字符串可以清晰地定位小数点位置和需要处理的数字位,方便进行四舍五入操作。

———(如有问题,欢迎评论区提问)———

相关文章:

  • Xdocreport实现根据模板导出word
  • 图论整理复习
  • 国标GB28181视频平台EasyCVR如何搭建汽车修理厂远程视频网络监控方案
  • std::string` 类
  • 数据库数据恢复——sql server数据库被加密怎么恢复数据?
  • Dify+DeepSeek能做出什么来?快速构建可扩展的 AI 应用
  • 程序化广告行业(76/89):行业融资全景剖析与代码应用拓展
  • 电力交易中长期市场基础知识
  • 高并发的业务场景下,如何防止数据库事务死锁
  • image tokenizer 原理和代码
  • C++ 知识笔记
  • 项目整合管理_项目管理计划和项目文件
  • 用css画一条弧线
  • 数据库分表分库
  • 为什么AI系统习惯性“画大饼”?:深度解析算法逻辑与技术瓶颈
  • docker部署postgresql
  • Notepad++安装Markdown实时预览插件
  • 国产Linux统信安装mysql8教程步骤
  • 工厂模式(简单工厂,工厂方法,抽象工厂)
  • 【14】Strongswan watcher详解1
  • 加强网站人才建设/seo一个月工资一般多少
  • 重庆营销型网站建设价格/seo优化搜索结果
  • 工信部网站域名备案查询/seo是什么seo怎么做
  • 网站服务提供商/百度的特点和优势
  • 专业做蛋糕视频网站/百度搜索入口
  • 网站设计制作公司推荐/网站查询工具seo