C语言算法入门【码蹄集进阶塔】MT2181永恒之2——进位制

C语言算法入门【码蹄集进阶塔】MT2181永恒之2——进位制

题目描述

小码哥被困扰住了,因为电脑它又双妤双坏了!!! 现在还是想让你算一些东西:给出一个十六进制数k,求出在1到k中“2"出现了多少次(1到k都用十六进制表示),其中k在十六进制下不大于10位。 输出的答案请对99999989取余。
输入格式:
输入格式:
第一行一个十进制整数n(1 ≤ n ≤ 10),表示k的位数。
第二行一个十六进制数k。
.
输出格式: 一个十进制整数,表示1到k中“2”出现的次数对99999989取余的答案。

样例

输入:
2
2A
.
输出: 14

备注:

首先将从1到2A的所有数全部以十六进制的方式列出来:
1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20,21,22,23,24,25,26,27,28,29,2A。
我们可以数得“2”一共出现了14次。

MT2181这道题主要是考验数学思维进位制

题目中是16进制,我们先按照10进制举例

在这里插入图片描述
在这里插入图片描述

还有一个特殊情况需要说明

当遍历到这个数的首位和末位时,前缀或者后缀都为0,所以要在每次循环遍历之前要对pre和suf进行初始化

对于16进制和10进制思维完全相同,主要是控制好前缀pre和后缀suf的进位关系就好了

到此,这个题的核心思想我们就讲完了,以下是代码和核心代码块的解释。

参考代码

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

#define int long long
int p[15] = {1}, a[15], n, ans;
char s[15];
const int mod = 99999989;

int main() {
    scanf("%lld %s", &n, s); // 使用%lld读取long long类型,%s读取字符串
    for (int i = 1; i <= n; i++)
        p[i] = p[i - 1] * 16;
    for (int i = 0; i < n; i++) {
        if (s[i] >= '0' && s[i] <= '9')
            a[i] = s[i] - '0';
        else
            a[i] = s[i] - 'A' + 10;
    }
    for (int i = 0; i < n; i++) {
        int pre = 0, suf = 0;
        for (int j = 0; j <= i - 1; j++)
            pre = (pre * 16 + a[j]) % mod; // 确保结果在模数范围内
        for (int j = i + 1; j < n; j++)
            suf = (suf * 16 + a[j]) % mod; // 确保结果在模数范围内
        if (a[i] > 2)
            ans = (ans + (pre + 1) * p[n - i - 1] % mod) % mod;
        else if (a[i] < 2)
            ans = (ans + pre * p[n - i - 1] % mod) % mod;
        else
            ans = (ans + suf + 1 + pre * p[n - i - 1] % mod) % mod;
    }
    printf("%lld\n", ans); // 使用%lld打印long long类型
    return 0;
}

代码块解释

    for (int i = 1; i <= n; i++)
        p[i] = p[i - 1] * 16;
  • 这是一个循环,用于计算16的幂次,并存储在 p 数组中。p[0] 已经初始化为1,所以从 p[1] 开始计算。
    for (int i = 0; i < n; i++) {
        if (s[i] >= '0' && s[i] <= '9')
            a[i] = s[i] - '0';
        else
            a[i] = s[i] - 'A' + 10;
    }
  • 这个循环将输入的十六进制字符串 s 转换为对应的整数值数组 a。如果字符是 ‘0’ 到 ‘9’,则直接减去 ‘0’;如果是 ‘A’ 到 ‘F’,则减去 ‘A’ 并加上10。
    for (int i = 0; i < n; i++) {
        int pre = 0, suf = 0;
        for (int j = 0; j <= i - 1; j++)
            pre = (pre * 16 + a[j]) % mod; // 确保结果在模数范围内
        for (int j = i + 1; j < n; j++)
            suf = (suf * 16 + a[j]) % mod; // 确保结果在模数范围内
        if (a[i] > 2)
            ans = (ans + (pre + 1) * p[n - i - 1] % mod) % mod;
        else if (a[i] < 2)
            ans = (ans + pre * p[n - i - 1] % mod) % mod;
        else
            ans = (ans + suf + 1 + pre * p[n - i - 1] % mod) % mod;
    }
  • 这个循环遍历每位字符,并计算前缀 pre 和后缀 suf 的十六进制值。pre 是从字符串开始到当前字符之前的十六进制值,suf 是从当前字符之后到字符串结束的十六进制值。
  • 根据当前字符的值,更新 ans。如果当前字符的值大于2,则将 pre 加1后乘以 p[n - i - 1] 并取模;如果小于2,则直接将 pre 乘以 p[n - i - 1] 并取模;如果等于2,则将 suf 加1后加上 pre 乘以 p[n - i - 1] 并取模。

    printf("%lld\n", ans); // 使用%lld打印long long类型
    return 0;
  • printf("%lld\n", ans);:使用 printf 函数打印最终计算的结果 ans

结语:

写这篇博客旨在记录我自己在学习算法的路上遇到的经典或者有趣的算法,并将它们分享给大家,以后可能还会持续更新,如果有觉得写的不错同学可以赏个赞再走哦。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值