C++课后习题训练记录Day161

1.练习项目 :

问题描述

地球上,我们习惯用十进制数字来记录万物,从个位、十位,逐级向上构成了我们熟悉的自然数体系。

然而,在遥远的蓝桥星,数字的排列和解读方式却与我们截然不同。蓝桥星人并不单纯地以数值大小来衡量一个数字,他们更注重数字内部蕴含的 "节奏感"。因此,对他们而言,任何一个有效的数字,其从左到右每一位上的数字奇偶性都必须是交替出现的。

例如,对于 10 这个数字,其十位是奇数 1,个位是偶数 0,呈现奇偶交替,因此 10 是个有效的数字。而对于 13 这个数字,其十位是奇数 1,个位也是奇数 3,不符合奇偶交替的条件,因此 13 不是个有效的数字。

根据这个规则,蓝桥星的数字序列从 10 开始,依次为 10,12,14,16,18,21,23,25,27,29,30,...。

只不过,随着文明的发展,蓝桥星人需要一种方法,来快速找到第 N 个符合这种奇偶交替规则的数字,以满足其日益增长的数字处理需求。现在,请你帮助蓝桥星人,编写程序找出并输出第 N 个符合奇偶交替规则的数字。

输入格式

输入包含一个正整数 N,表示需要查找第 N 个符合规则的数字。

输出格式

输出一个整数,表示第 N 个符合奇偶交替规则的数字。

2.选择课程

在蓝桥云课中选择题库,选择题号21258并开始练习。

3.开始练习

(1)源码 :

#include<bits/stdc++.h>

using namespace std;

using ll=long long;

int main()

{

ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

ll a=9*5,b=2,n;

cin>>n;

while(n>a){

n-=a;

b++;

a*=5;

}

n--;

a/=9;

bool las=(n/a+1)%2;

cout<<n/a+1;

n%=a;

for(int i=1;i<b;i++){

a/=5;

cout<<n/a*2+1-las;

las=!las;

n%=a;

}

return 0;

}

(2)检验结果

对此代码进行检验,检验后无报错,提交此代码,判题结果为正确100分。

(3)练习心得:

一、问题模型化

我们只考虑至少两位数的正整数。对于一个 k 位数,从左到右每一位记为 d1d2...dk​,其中 d1​ 是最高位。

规则:相邻两位的奇偶性不同,即:

  • 若 di​ 是奇数,则 di+1​ 必须是偶数;

  • 若 di​ 是偶数,则 di+1 必须是奇数。

计数

  • 最高位 d1​ 不能是 0,因此它有 9 种选择:1~9。

  • 对于之后的每一位 di​(i≥2),它必须与 di−1​ 奇偶性相反。

    • 假设 di−1​ 是奇数,则 di​ 必须是偶数,可选 0,2,4,6,8,共 5 种。

    • 假设 di−1​ 是偶数,则 di​ 必须是奇数,可选 1,3,5,7,9,也是 5 种。

  • 因此,不管前一位的奇偶性如何,下一位始终有 5 种选择

  • 所以一个 k 位数的合法个数为:

    9×5k−1

    (最高位 9 种,后面 k−1 位各 5 种)

例子

  • 2 位数:9×51=45 个

  • 3 位数:9×52=225 个

  • 4 位数:9×53=1125 个


二、算法步骤概览

我们要找第 N 个符合条件的数,算法分两步:

  1. 确定这个数是几位数

    逐步减去 2 位数、3 位数......的个数,直到剩余的数量不超过当前位数的总数。

  2. 逐位求出数字本身

    利用"分组"的思想,将目标数字在当前位置的取值范围内进行索引定位,通过除法确定它属于第几组,再根据奇偶约束转换为实际数字。

代码就精确实现了这个两步策略。


三、逐行代码深度解析

第一步:初始化变量,读入 N

cpp

复制代码
long long a = 45, b = 2, n;
cin >> n;
  • a = 45 表示当前考虑的位数(即 b 位数)的总个数 。初始 b=2 代表两位数,两位数的总数是 45,所以 a 初始化为 45。

  • b 是当前位数。

  • n 存放输入,表示我们要找第 n 个(从 1 开始计数)。


第二步:确定最终答案的位数

cpp

复制代码
while (n > a) {
    n -= a;
    b++;
    a *= 5;
}

这个循环的含义是:

  • 如果 n 比当前位数的总数 a 还要大,说明目标数字不在 b 位数里,应该跳过所有 b 位数。

  • 执行 n -= a:从 n 中减去 b 位数的所有数量,这样 n 就变成了更高位数序列中的第几个

  • b++:位数加 1。

  • a *= 5:更新新位数的总数。因为从 kk 位变成 k+1k+1 位,总数乘 5(多了一位,多一个 5 选择)。

循环结束后的状态:

  • b 就是答案的位数。

  • n 表示b 位数中的第 n(从 1 开始计)。

  • a 此时是 b 位数的总个数,即 9 * 5^(b-1)


第三步:准备"逐位构造"

cpp

复制代码
n--;
a /= 9;
  • n-- :将 n 转换为 0‑based 索引 ,取值范围变成 0a-1

    为什么要这样做?因为接下来我们要用除法分组,0‑based 更方便:

    例如 n / group_size 会直接给出组号(0,1,2,...),这正是我们想要的偏移量。

  • a /= 9 :此时 a = 9 * 5^(b-1),除以 9 后 a = 5^(b-1)

    这个 a 的意义变了:现在它代表去掉最高位后,剩下 b-1 位能构成的所有组合数量

    最高位有 9 种选择,剩下位有 5^(b-1) 种组合,所以总组合 9 * a。当前偏移 n(0‑based)可以写成:

    n=i×a+rn=i×a+r

    其中 i = n / a(取值范围 0~8)对应最高位的 9 种选择中的第几个,r = n % a 是去掉最高位后剩余部分的偏移量。


第四步:求出最高位

cpp

复制代码
bool las = (n / a + 1) % 2;
cout << n / a + 1;
n %= a;
  1. 确定最高位数字

    n / a 是索引 i(0~8),那么最高位数字就是 i + 1(因为最高位从 1 到 9)。

    cout << n / a + 1; 直接输出这个数字。

  2. 记录最高位的奇偶性

    (n / a + 1) % 2

    • 如果数字是偶数,% 2 为 0;

    • 如果是奇数,% 2 为 1。

      把它存到 las 变量中,las 代表"上一位的奇偶性"(1 表示奇数,0 表示偶数),这个信息将用于决定下一位应该填奇数还是偶数。

  3. 更新偏移量 n

    n %= a; 此时 n 变成 r,即剩余 b-1 位中的偏移量(0‑based)。后续我们将在剩余位上重复类似的过程。


第五步:逐位构造剩余的 b-1

cpp

复制代码
for (int i = 1; i < b; i++) {
    a /= 5;
    cout << n / a * 2 + 1 - las;
    las = !las;
    n %= a;
}

这个循环运行 b-1 次,每次输出一位数字。

循环内部详解:
  • a /= 5;

    此时 a 代表"当前位之后剩余位数能构成的所有组合数量"。

    进入循环时,a5^(b-1)。第一次循环 a /= 5a = 5^(b-2),第二次 5^(b-3),以此类推,直到最后 a=1(剩下 0 位,只有 1 种可能,即没有选择了)。

  • n / a

    当前位有 5 种可能的选择(因为必须与上一位奇偶性相反)。n / a 就是当前位在它的 5 种可能取值中的索引 (0~4)。

    n 是当前剩余的偏移量(0‑based),a 是剩余组合数,所以能确定当前位是哪个选项。

  • cout << n / a * 2 + 1 - las;

    这是将索引 j = n/a 转换成实际数字的公式。

    解释:

    • 如果上一位 las 是 1(奇数),则本位必须填偶数,可选 {0,2,4,6,8}。

      这些偶数可以表示为 2 * j,其中 j = 0..4 分别对应 0,2,4,6,8。

    • 如果上一位 las 是 0(偶数),则本位必须填奇数,可选 {1,3,5,7,9}。

      这些奇数可以表示为 2 * j + 1j = 0..4 对应 1,3,5,7,9。

      统一成一条公式:

    数字={2j+1若上一位是偶数 (las=0)2j若上一位是奇数 (las=1)数字={2j+12j若上一位是偶数 (las=0)若上一位是奇数 (las=1)

    合并为 2j + 1 - las

    • las=02j+1(奇数)

    • las=12j(偶数)

      完美对应。

  • las = !las;

    翻转奇偶性,因为下一位又需要与前一位相反。!las 将 0 变 1,1 变 0。

  • n %= a;

    更新 n 为当前位选定后,剩余低位部分的新偏移量。


四、完整示例追踪:N = 10

目标:找到序列中第 10 个数。已知序列前 10 个为:

10(1), 12(2), 14(3), 16(4), 18(5), 21(6), 23(7), 25(8), 27(9), 29(10)

我们跟着代码走:

  1. 初始 a=45, b=2, n=10

  2. while (n > a)10 > 45 否,跳过。所以答案仍为 2 位数,n=10

  3. n--n=9a /= 9a = 5(即 52−1=552−1=5)。

  4. 最高位:

    • i = n / a = 9 / 5 = 1

    • 最高位数字 = 1 + 1 = 2,输出 2

    • las = (2) % 2 = 0(偶数)。

    • n %= an = 9 % 5 = 4

  5. 循环 i=1 (因为 b=2,循环只一次):

    • a /= 5a = 1

    • j = n / a = 4 / 1 = 4

    • 下一位数字 = 4 * 2 + 1 - las = 8 + 1 - 0 = 9,输出 9

    • las = !0 = 1(无后续影响)。

    • n %= an = 0

  6. 最终结果 29,与预期一致。

注意每段代码末尾的分号是否存在 ,如不存在则需即使补充;输入法 是否切换 为英语模式;语法是否错误。

相关推荐
nzz_1712142 小时前
PHP程序员转型AI岗位指南:核心技能、北京就业市场与转型路径分析
开发语言·人工智能·php
Risehuxyc2 小时前
C# 将doc转换为docx
开发语言·c#·xhtml
是个西兰花2 小时前
Linux:深入解析Linux线程原理与实现
linux·运维·c++·线程·互斥锁
多加点辣也没关系3 小时前
JavaScript|第24章:事件循环与并发模型
开发语言·javascript·ecmascript
ん贤3 小时前
怎么设计,才算一个优秀审计模块
大数据·开发语言·设计·审计
l1t3 小时前
测试用rust重写的postgresql: pgrust
开发语言·postgresql·rust
2501_948106913 小时前
计算机毕业设计之jsp-智慧旅游分享平台
java·开发语言·spark·汽车·课程设计·旅游
阿里云云原生4 小时前
Agent 不再是“玩具”!AgentScope Java 2.0 GA 发布:构建企业级分布式智能体底座
java·开发语言·分布式·agentscope
BIM云平台开发4 小时前
【App.vue里跟踪页面跳转和用户ID】
开发语言·前端·javascript
NiceCloud喜云4 小时前
ClaudeAPI 接入 n8n / Dify / Open WebUI 实战:Base URL 配置、调用示例与排错
开发语言·ai·chatgpt·aigc