C++详解NOI题:[NOIP2021] 报数

news2025/1/17 1:02:25

文章目录

  • 前言
  • 一、题目
  • 二、暴力解题步骤(50分)
  • 三、打表防坑解题(100分)
  • 总结


前言

受不了CSDN每日一练的在线竞赛系统了,bug多就算了,勉强能用,可那些题目的神描述,到处是错。所以找点别的题来玩,看到一道NOI的题挺有意思,就试着解解。
在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、题目

题目描述:
报数游戏是一个广为流传的休闲小游戏。参加游戏的每个人要按一定顺序轮流报数,但如果下一个报的数是 7 的倍数,或十进制表示中含有数字 7,就必须跳过这个数,否则就输掉了游戏。

在一个风和日丽的下午,刚刚结束 SPC20nn 比赛的小 r 和小 z 闲得无聊玩起了这个报数游戏。但在只有两个人玩的情况下计算起来还是比较容易的,因此他们玩了很久也没分出胜负。此时小 z 灵光一闪,决定把这个游戏加强:任何一个十进制中含有数字 7 的数,它的所有倍数都不能报出来!

形式化地,设 p(x) 表示 x 的十进制表示中是否含有数字 7,若含有则 p(x)=1,否则 p(x)=0。则一个正整数 x 不能被报出,当且仅当存在正整数 y 和 z ,使得 x=yz 且 p(y)=1。

例如,如果小 r 报出了 6 ,由于 7 不能报,所以小 z 下一个需要报 8;如果小 r 报出了 33,则由于 34=17×2,35=7×5 都不能报,小 z 下一个需要报出 36 ;如果小 r 报出了 69,由于 70∼79 的数都含有 7,小 z 下一个需要报出 80 才行。

现在小 r 的上一个数报出了 x,小 z 想快速算出他下一个数要报多少,不过他很快就发现这个游戏可比原版的游戏难算多了,于是他需要你的帮助。当然,如果小 r 报出的 x 本身是不能报出的,你也要快速反应过来小 r 输了才行。

由于小 r 和小 z 玩了很长时间游戏,你也需要回答小 z 的很多个问题。

输入格式,最大数T <= 2x105, x <= 107
第一行,一个正整数 T 表示小 z 询问的数量。

接下来 T 行,每行一个正整数 x,表示这一次小 r 报出的数。

输出格式
输出共 T 行,每行一个整数,如果小 r 这一次报出的数是不能报出的,输出 −1,否则输出小 z 下一次报出的数是多少。

示例 输入: 4 6 33 69 300 输出:8 36 80 -1

二、暴力解题步骤(50分)

一看这种题,第一个想法肯定是暴力先来一波嘛~,说干就干!嗯,这个平台是用的文件输入输出的形式解题的,就是说得自己写从文件读数据,然后计算完成后自己写入一个文件当交作业。也是有点折腾人的哈,因为笔者这代码结合在一起了,懒得拆了,一起贴上。

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

bool check(int num){
    bool res;
    string sn = to_string(num);
    int indx = sn.find('7');
    if(indx != string::npos) {
        return false;
    }else res = true;   
    return res;
}

bool resolve(int num){
    bool res;
    for (int i=1; i<num/2+1; ++i){
        if(num%i==0){
            if (check(i) && check(num/i)) {
                res = true;
            }else{
                res = false;
                break;
            }
        }
    }  
    return res;
}

int main(){

    vector<int> vec;
    ifstream fin;
    fin.open("number.in", ios::in);
    if(fin.is_open()){
        string buf;
        while (getline(fin, buf)){
            vec.push_back(stoi(buf));
        }
        fin.close();
    } 
    int n = vec[0];
    vec.erase(vec.begin());
    
    int tmp;
    ofstream fout;
    fout.open("number.out");
    for (int val :vec){
        //cout << "in: " << val << endl; 
        if (resolve(val)){
            tmp = val;
            while(tmp){
                tmp++;
                if(resolve(tmp)){
                    break;
                }else continue;
            }
        }else{
            tmp = -1;
        }
        fout << tmp << endl;
        fout.flush();
    }
    return 0;
}

反正这平台啥都得自己写,费事很。所以这题的暴力解法,费了我好大劲的…其实main函数中那么一大串都是在读文件,写文件。有用的就是for循环把数据送给resolve处理,这个函数是为了分解出因数,分解后交给check函数检测是否有7,有就中止,没有就继续。其实这也做了一些小优化的,不然50分都不一定有,比如将分解因数上限取为一半,除数和商都是因数,一起检测什么的。最后回复给main函数。一个个问题测试一个个写入输出文件就搞定了,本机测试很好嘛~ 拿到平台上一测,hehe!too young too simple 了,50分。很显然是有特大数据要处理的,这种笨法子能过,那noi就没含金量了。那就只能改了,本着以空间换时间的思想,咱先把所有含7的数算出来,放到数组对应的下标中。问题文件中要啥咱就去查一下不就成了。换打表思想来做了试试,回想一下小学数学就开干:

三、打表防坑解题(100分)

很显然我这maximum的值略小了一个数量级,笔者这老苹果MAC不给分这么多连续内存… 这种题的这种解法也是让人无语的,这比赛真有这么多内存给用吗?为了验证一下,笔者换 了一台win电脑,嗯~ 那个电脑内存够大,确实没有问题,通过计算108也就1千万(题目描述是107,实际是8次方,C++中1e8是一千万),bool值才1个字节,算起来也就十M左右。

bool* finded(){
    int const maximum = 1000009;  //中间少了个0
    bool table[maximum];
    for (int i=0; i< maximum; ++i){
        table[i] = 1;
    }
    int tmp;
    for (int i=7; i<maximum; ++i){   // 求出所有含7的数的倍数
        tmp = i;
        while (tmp){
            if (tmp%10==7){
                int j = 1;
                while(i*j<maximum){
                    //cout << i*j << " ";
                    table[i*j] = 0;
                    j++;
                }
            }
            tmp /= 10;
        }
    }
    bool* ptr = table;
    return ptr;
}

int main(){   
    bool *table = finded();
    vector<int> vec;
    ifstream fin;
    fin.open("number.in", ios::in);
    if(fin.is_open()){
        string buf;
        while (getline(fin, buf)){
            vec.push_back(stoi(buf));
        }
        fin.close();
    } 
    int n = vec[0];
    vec.erase(vec.begin());
    
    int tmp;
    ofstream fout;
    fout.open("number.out");

    for (int val :vec){
        std::cout << "in= " << val << endl;
        if (table[val]){
            tmp = val+1;
            while(tmp){
                string sn = to_string(tmp);   // 防6999999
                int ind = sn.find("7");
                int sl = sn.length();
                if(ind != string::npos && sl-ind >1){
                    tmp = tmp + pow(10, sl-1-ind) - stoi(sn.substr(ind+1, sl));
                }           
                if (table[tmp]){
                    break;  
                }else tmp++;               
            }
        }else{
            tmp = -1;
        }     
        fout << tmp << endl;
        fout.flush();
    }
    return 0;
}

总体思路也不复杂,首先建了一个bool数组,将含7的数的倍数都做为下标,表示为false。其余不含7的表示为true。如此只需在回答问题前查一下数组即可知道是否可以回答,同理问题也要先检查,如果在表中表示为false的回答-1。

为了避免6999999这种特大坑,笔者想了个办法:即将回答转成字符串,tmp = tmp + pow(10, sl-1-ind) - stoi(sn.substr(ind+1, sl)); 这里的判断比较复杂,但这是笔者想到的最好办法了!只有7在十位以上才直接加上10的n次方,一次性跳过70,700,7000…,这种类型的数字,以免不停查数组。

顺便说一下,在某平台编译不了。应该是bool数组的指针问题,好像不让这么写。才吐槽完CSDN的在线编译器烂,这还有更烂的!我改成直接copy数组,不报段错误了。又给我说table[val]的下标无效。笔者把那个finded函数去了,也写到main里面就好了,也就不用考虑指针传递的事情了。测试数据肯定是没问题的,但是…输入数据只过了70,说是中间又出了段错误!想了想,是笔者犯傻了,忘记把maximum值改大了,确实会段错误,查表的时候超出范围了嘛,再加上个零后。完美通过!

如果抄作业的话,改进某平台的编译器,把后一个tmp变量写到for里面去。这里的写法在vscode中编译通过,也没有warning,小时候老师教导过:一个函数就干一件事!后面的解题代码没有写头文件,和暴力解法比要多个cmath引入,是计算pow用的。


总结

这题说有多难嘛,还真算不上,不过代码是真的长啊~ 而且坑也是比较大的,其实在笔者想来这种题真是纯为了竞赛出的,实际意义也是真的不大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/557145.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端面试知识点总结

前言&#xff1a; 博主突击两个月八股拿到美团&#xff08;基础研发&#xff09;&#xff0c;腾讯&#xff08;IEG&#xff09;&#xff0c;百度&#xff08;搜索部门&#xff09;暑期实习offer call&#xff0c;这是我学习过程中整理的前端知识点&#xff0c;内容有些多&#…

【13900k】i9 核显升级驱动

这里写自定义目录标题 官方的助手不能用显卡控制中心提示最新的更新搜索显卡 intel uhd graphics 770 手动下载安装自定义音频为啥也要卸载&#xff1f;新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片…

网络编程——嵌入式(驱动)软开基础(六)

1、简述TCP三次握手的过程。 (1)第一次握手:客户端创建传输控制块,然后向服务器发出连接请求报文(将标志位SYN置1,随机产生一个序列号seq=x),接着进入SYN-SENT状态。 (2)第二次握手:服务器收到请求报文后由SYN=1得到客户端请求建立连接,回复一个确认报文(将标志…

进程启动后到加载Activity的流程源码解析(基于安卓版本28)

文章目录 源码解析总体时序图关键类解析ActivityThreadApplicationThreadInstrumentationClientTransactionActivityStackSupervisorActivityRecord梳理概述源码流程梳理 源码解析 总体时序图 关键类解析 只针对流程中用到的关键类进行解析。 ActivityThread 注意其父类是&…

ES6升级之路:探究模板字符串、startsWith()方法和endsWith()方法、repeat()等新特性。

模版字符串 ES6新增的创建字符串的方式,使用反引号定义 示例 <script>// 1.模板字符串可以解析变量 ${}显示变量的值let name 张三;let sayHello HEllo,我的名字叫${name};console.log(name);console.log(sayHello);let result {name: "zhangsan",age: 20…

Java【TCP 协议2】确认应答、超时重传机制

文章目录 前言一、确认应答1, 什么是确认应答2, 序列号和确认应答号 二、超时重传1, 什么是超时重传 总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5; JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系…

python基础(循环语句 while循环、break、continue,字符格式化,运算符)

1. while循环 【1】语法&#xff1a; while 条件:.........举例&#xff1a; print("123") while 条件:......... print(456)【2】循环语句的基本使用 示例1&#xff1a; print("开始") while True:print("hello world") print("结束&…

约瑟夫问题的环形链表实现[Java]

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐如果觉得文章写的不错&#xff0c;欢迎点个关注一键三连&#x1f609;有写的不好的地方也欢迎指正&#xff0c;一同进步&#x1f601;…

(全网最详细攻略)【Crypto++】在Visual studio2022中运行Cryptopp

文章目录 前言一、Cryptopp是什么&#xff1f;1. Cryptopp&#xff08;CRYPTO&#xff09;官方文档wiki 二、下载Cryptopp2. Crypto下载地址3. 下载PEM包 三、在VS2022中使用Cryptopp库4. 处理crypto源文件5. 在VS2022项目中使用crypto库 四、运行代码后一些关于c的错误总结 前…

单片机--中断实验练习

【1】实验要求&#xff1a; STM32上电LED&#xff08;PB0&#xff09;灯亮 &#xff0c;当检测到按键&#xff08;PA8&#xff09;按下时处理中断事件&#xff0c;变量i 扩展&#xff1a;知识点 响应优先级->在两个中断同时触发时&#xff0c;且这两个中断的优先级相同&a…

SpringBootWeb入门

1. SpringBootWeb快速入门 1.1 需求 需求&#xff1a;基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求/hello后&#xff0c;给浏览器返回字符串 “Hello World ~”。 1.2 开发步骤 第1步&#xff1a;创建SpringBoot工程项目 第2步&#xff1a;定义HelloCon…

chatgpt赋能Python-python_docx_目录

简介 Python是一种非常优秀的编程语言&#xff0c;主要用于数据分析、机器学习、人工智能等领域。在这些领域中&#xff0c;常常需要使用文档处理相关的工具&#xff0c;例如Microsoft Word。在使用Python编程的过程中&#xff0c;文档处理是非常常见的操作&#xff0c;因此Py…

AC规则-1

本文主要参考规范 GPD_Secure Element Access Control_vxxx.pdf OMA 架构 基本定义 GP(GlobalPlatform)定义了一套允许各应用提供方独立且安全地管理其在SE上的应用的安全框架&#xff0c;而AC(Access Control)&#xff0c;顾名思义&#xff0c;是对外部应用进行SE上应用访问…

pyinstaller 打包 ! pyinstaller 打包路径问题!wordcloud打包缺失stopwords文件

wordcloud打包缺失stopwords文件 错误描述 Unhandled exception in script Failed to execute scriptmain due to unhandled exception: (Errno 2] No such file or directory: C:\Users\VADMINI\appDatallLocal\Templ\2\ME186322\wordcloud\stopwords解决办法 找到安装word…

实验室基础操作

一&#xff1a;FZmotion。 1&#xff1a;查看相机是否加入成功。 2&#xff1a;添加蒙版。 3&#xff1a;选择标定杆类型。500mm 4&#xff1a;标定。 5&#xff1a;数据传输。 二&#xff1a;MotionBuilder。 1&#xff1a;所使用插件。 2&#xff1a;fzmotion插件安装。 Mo…

chatgpt赋能Python-python_errno22

Python的errno22错误——引起问题的原因及解决方法 如果你使用过Python编程语言进行过开发&#xff0c;你可能会像其他开发者一样遭遇过errno22错误。这种错误通常会导致程序崩溃或者无法正常运行。在这篇文章里&#xff0c;我们将深入了解errno22错误的原因&#xff0c;并提供…

【数据结构】C--顺序表1.0版本(本文非常适合小白观看,已尽力详解,以及图解也是尽量列举)

目录 0.前言 什么是数据结构&#xff1f; 1.逻辑结构: 1.1线性结构: 1.2非线性结构: (1)集合 (2)树形结构 (3)图形结构或者网状结构 2.存储结构 一.线性表 二.顺序表 顺序表与数组的关系:(非常容易混淆) 1.静态顺序表&#xff1a;使用定长数组存储元素 2.动态顺序表…

MySQL中auto_increment有什么作用?(IT枫斗者)

MySQL中auto_increment有什么作用&#xff1f; 问题来源 很多时候&#xff0c;MySQL语句中会出现【auto_increment】这个词汇&#xff0c;大多数时候&#xff0c;表都是自动生成的&#xff0c;刚开始学习MySQL数据库时会学习到&#xff0c;后来&#xff0c;渐渐地可能会忘记&…

Windows安装VirtualBox教程(图文版)

VirtualBox是一款免费的虚拟化软件&#xff0c;可以在一台计算机上运行多个操作系统。它可以在Windows、Linux、Mac OS X和Solaris等操作系统上运行。VirtualBox支持多种虚拟硬件设备&#xff0c;包括网络适配器、USB控制器、显卡等。用户可以通过VirtualBox创建一个虚拟的计算…

同个前端页面,在手机端和PC端打开,访问到的资源有可能不是同一个

记录项目遇到的问题&#xff0c;问题表现为&#xff1a; 1、用手机端和PC端打开同一个前端页面&#xff0c;通讯到达的后端服务却不是同一个 排查&#xff1a; 1、确认手机端和PC端打开后&#xff0c;实际访问的前端资源并不是同一个 2、手机端配置的socket端口有误&#x…