1

GESP认证C++编程真题解析 | 202309 四级

​欢迎大家订阅我的专栏:算法题解:C++与Python实现
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!

专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。

适合人群:

  • 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
  • 希望系统学习C++/Python编程的初学者
  • 想要提升算法与编程能力的编程爱好者

附上汇总帖:GESP认证C++编程真题解析 | 汇总


编程题

B3869 进制转换

【题目来源】

洛谷:[B3869 GESP202309 四级] 进制转换 - 洛谷

【题目描述】

\(N\) 进制数指的是逢 \(N\) 进一的计数制。例如,人们日常生活中大多使用十进制计数,而计算机底层则一般使用二进制。除此之外,八进制和十六进制在一些场合也是常用的计数制(十六进制中,一般使用字母 A 至 F 表示十至十五;本题中,十一进制到十五进制也是类似的)。

在本题中,我们将给出 \(N\) 个不同进制的数。你需要分别把它们转换成十进制数。

【输入】

输入的第一行为一个十进制表示的整数 \(N\)。接下来 \(N\) 行,每行一个整数 \(K\),随后是一个空格,紧接着是一个 \(K\) 进制数,表示需要转换的数。保证所有 \(K\) 进制数均由数字和大写字母组成,且不以 \(0\) 开头。保证 \(K\) 进制数合法。

保证 \(N \le 1000\);保证 \(2 \le K \le 16\)

保证所有 \(K\) 进制数的位数不超过 \(9\)

【输出】

输出 \(N\) 行,每一个十进制数,表示对应 \(K\) 进制数的十进制数值。

【输入样例】

2
8 1362
16 3F0

【输出样例】

754
1008

【算法标签】

《洛谷 B3869 进制转换》 #进制# #GESP# #2023#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

#define int long long  // 使用长整型

int n;  // 测试用例数量

/**
 * 将指定进制的字符串转换为十进制整数
 * 支持2-16进制
 * @param x 进制(2-16)
 * @param t 要转换的字符串
 * @return 对应的十进制整数
 */
int calc(int x, string t)
{
    int res = 0;  // 存储转换结果
    
    // 遍历字符串的每个字符
    for (int i = 0; i < t.size(); i++)
    {
        if (t[i] <= '9')
        {
            // 处理数字字符 '0'-'9'
            // 霍纳法则:res = res * 进制 + 当前位的值
            res = res * x + (t[i] - '0');
        }
        else
        {
            // 处理字母字符 'A'-'F'
            // 将'A'-'F'转换为10-15
            res = res * x + (t[i] - 'A' + 10);
        }
    }
    
    return res;
}

signed main()  // 因为使用了#define int long long
{
    // 输入测试用例数量
    cin >> n;
    
    // 处理每个测试用例
    while (n--)
    {
        int k;      // 进制
        string s;   // 要转换的字符串
        
        cin >> k >> s;
        
        // 调用calc函数进行进制转换并输出结果
        cout << calc(k, s) << endl;
    }
    
    return 0;
}

【运行结果】

2
8 1362
754
16 3F0
1008

B3870 变长编码

【题目来源】

洛谷:[B3870 GESP202309 四级] 变长编码 - 洛谷

【题目描述】

小明刚刚学习了三种整数编码方式:原码、反码、补码,并了解到计算机存储整数通常使用补码。但他总是觉得,生活中很少用到 \(2^{31}-1\) 这么大的数,生活中常用的 \(0\sim 100\) 这种数也同样需要用 \(4\) 个字节的补码表示,太浪费了些。
热爱学习的小明通过搜索,发现了一种正整数的变长编码方式。这种编码方式的规则如下:

  1. 对于给定的正整数,首先将其表达为二进制形式。例如,\((0)_{\{10\}}=(0)_{\{2\}}\)\((926)_{\{10\}}=(1110011110)_{\{2\}}\)
  2. 将二进制数从低位到高位切分成每组 \(7\) bit,不足 \(7\)bit 的在高位用 \(0\) 填补。例如,\((0)_{\{2\}}\) 变为\(0000000\) 的一组,\((1110011110)_{\{2\}}\) 变为 \(0011110\)\(0000111\) 的两组。
  3. 由代表低位的组开始,为其加入最高位。如果这组是最后一组,则在最高位填上 \(0\),否则在最高位填上 \(1\)。于是,\(0\) 的变长编码为 \(00000000\) 一个字节, \(926\) 的变长编码为 \(10011110\)\(00000111\) 两个字节。

这种编码方式可以用更少的字节表达比较小的数,也可以用很多的字节表达非常大的数。例如,\(987654321012345678\) 的二进制为 \((0001101 \ 1011010 \ 0110110 \ 1001011 \ 1110100 \ 0100110 \ 1001000 \ 0010110 \ 1001110)_{\{2\}}\),于是它的变长编码为(十六进制表示) CE 96 C8 A6 F4 CB B6 DA 0D,共 \(9\) 个字节。

你能通过编写程序,找到一个正整数的变长编码吗?

【输入】

输入第一行,包含一个正整数 \(N\)。约定 \(0\le N \le 10^{18}\)

【输出】

输出一行,输出 \(N\) 对应的变长编码的每个字节,每个字节均以 \(2\) 位十六进制表示(其中, A-F 使用大写字母表示),两个字节间以空格分隔。

【输入样例】

0

【输出样例】

00

【算法标签】

《洛谷 B3870 变长编码》 #GESP# #2023#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1005;
int n;
string a[N];  // 存储分组后的二进制字符串

/**
 * 十进制转二进制字符串
 * @param x 十进制整数
 * @return 二进制字符串
 */
string DtoB(int x)
{
    string d = "0123456789ABCDEF";  // 数字字符表
    string ans = "";
    
    // 不断除以2,取余数
    while (x > 0)
    {
        ans = d[x % 2] + ans;  // 余数转换为'0'或'1',加到字符串前面
        x /= 2;
    }
    return ans;
}

/**
 * 二进制字符串转十进制
 * @param s 二进制字符串
 * @return 十进制整数
 */
int BtoD(string s)
{
    int ans = 0;
    // 使用霍纳法则:ans = ans * 2 + 当前位
    for (int i = 0; i < s.size(); i++)
    {
        ans = ans * 2 + (s[i] - '0');
    }
    return ans;
}

/**
 * 十进制转十六进制字符串
 * @param x 十进制整数
 * @return 十六进制字符串(至少两位,不足补0)
 */
string DtoH(long long x)
{
    string d = "0123456789ABCDEF", ans = "";
    
    // 特判0的情况
    if (x == 0) 
    {
        return "0";  // 注意:这里返回"0",但下面有补0处理
    }
    
    // 不断除以16,取余数
    while (x > 0) 
    {
        ans = d[x % 16] + ans;  // 余数转换为十六进制字符
        x /= 16;
    }
    
    // 如果结果只有一位,前面补0
    if (ans.length() == 1) 
    {
        ans = "0" + ans;
    }
    return ans;
}

signed main()
{
    cin >> n;
    
    // 特判输入为0的情况
    if (n == 0) 
    {
        cout << "00" << endl;
        return 0;
    }
    
    // 1. 十进制转二进制
    string s = DtoB(n);
    
    // 2. 从低位到高位,每7位一组(最后一组可能不足7位)
    int cnt = 0;  // 当前组内计数
    int cur = 1;  // 当前组号
    
    for (int i = s.size() - 1; i >= 0; i--)  // 从低位(字符串末尾)开始
    {
        cnt++;
        a[cur] += s[i];  // 将当前位添加到当前组
        
        // 每7位一组
        if (cnt == 7)
        {
            cur++;  // 开始新的一组
            cnt = 0;
        }
    }
    
    // 3. 最后一组如果不足7位,用0补齐
    while (a[cur].size() < 7)
    {
        a[cur] += '0';
    }
    
    // 4. 为每组添加最高位(标识位)
    for (int i = 1; i < cur; i++)  // 前cur-1组,最高位为1(表示还有后续)
    {
        a[i] += '1';
    }
    a[cur] += '0';  // 最后一组,最高位为0(表示结束)
    
    // 5. 反转每组字符串(因为是从低位开始构建的)
    for (int i = 1; i <= cur; i++)
    {
        reverse(a[i].begin(), a[i].end());
    }
    
    // 6. 将每组8位二进制转换为十六进制输出
    for (int i = 1; i <= cur; i++)
    {
        int t = BtoD(a[i]);      // 二进制转十进制
        // cout << "t " << t << endl;
        string s = DtoH(t);      // 十进制转十六进制
        cout << s << " ";        // 输出十六进制,以空格分隔
    }
    
    return 0;
}

【运行结果】

0
00
posted @ 2026-01-18 21:53  热爱编程的通信人  阅读(1)  评论(0)    收藏  举报