GESP 四级题单(困难) • 附免费参考答案

[GESP202309 四级] 变长编码

题目描述

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

  1. 对于给定的正整数,首先将其表达为二进制形式。例如, ( 0 ) { 10 } = ( 0 ) { 2 } (0)_{\{10\}}=(0)_{\{2\}} (0){10}=(0){2} ( 926 ) { 10 } = ( 1110011110 ) { 2 } (926)_{\{10\}}=(1110011110)_{\{2\}} (926){10}=(1110011110){2}

  2. 将二进制数从低位到高位切分成每组 7 7 7 bit,不足 7 7 7bit 的在高位用 0 0 0 填补。例如, ( 0 ) { 2 } (0)_{\{2\}} (0){2} 变为 0000000 0000000 0000000 的一组, ( 1110011110 ) { 2 } (1110011110)_{\{2\}} (1110011110){2} 变为 0011110 0011110 0011110 0000111 0000111 0000111 的两组。

  3. 由代表低位的组开始,为其加入最高位。如果这组是最后一组,则在最高位填上 0 0 0,否则在最高位填上 1 1 1。于是, 0 0 0 的变长编码为 00000000 00000000 00000000 一个字节, 926 926 926 的变长编码为 10011110 10011110 10011110 00000111 00000111 00000111 两个字节。

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

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

输入格式

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

输出格式

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

样例 #1

样例输入 #1

0

样例输出 #1

00

样例 #2

样例输入 #2

926

样例输出 #2

9E 07

样例 #3

样例输入 #3

987654321012345678

样例输出 #3

CE 96 C8 A6 F4 CB B6 DA 0D

参考答案

// longCode.cpp
// 字符串、进制
#include <iostream>
using namespace std;

long long n;
string h = "0123456789ABCDEF";

void p(int i)
{
	cout << h[i/16] << h[i%16] << " ";
}

int main()
{
    // 输入
	cin >> n;
	
	// 特例
	if (n == 0)
	{
	    cout << "00";
	    return 0;
	}
	
	// 模拟
	while(n)
	{
		int k = n % 128;
		n /= 128;
		if (n > 0)
		{
		    p(k+128); // 判断是否为最高位
		}
		else
		{
		    p(k);
		}
	}
	return 0;
}

代码解析

128 128 128 表示 2 7 2^7 27,因为我们一般七位分隔一次。

while 循环内部,首先使用取余操作 n % 128 得到当前位的十进制值,并保存在变量 k 中。然后使用除法操作 n /= 128 得到下一位的十进制值。这样,循环会逐步将整数 n 从右向左进行拆解,直到 n 变为 0 0 0 为止。

在执行完除法操作后,会判断当前位是否是最高位。如果 n > 0 成立,说明还有更高位的值需要处理,此时将 k+128 传入函数 p(),函数 p() 会将该值转换成十六进制字符输出。否则,直接将 k 传入函数 p(),将当前位的值转换成十六进制字符输出。

[GESP202403 四级] 做题

题目描述

小杨同学为了提高自己的实力制定了做题计划,在第 k k k 天时,他必须要完成 k k k 道题,否则他就会偷懒。

小杨同学现在找到了一个题库,一共有 n n n 套题单,每一套题单中有一定数量的题目。但是他十分挑剔,每套题单他只会使用一次,每一天也只能使用一套题单里的题目,之后那套题单就会弃之不用。对于每套题单,他不必完成题单内所有的题。

那么问题来了,小杨同学最多做题几天才偷懒呢?

输入格式

第一行,一个整数为 n n n,表示有多少套题单。
第二行 n n n 个整数 a 1 , a 2 , … a n a_1, a_2, \dots a_n a1,a2,an,分别表示每套题单有多少道题。

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

4
3 1 4 1

样例输出 #1

3

提示

数据规模与约定

对全部的测试数据,保证 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1n106 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1ai109

参考答案

// doTask.cpp
// 贪心
#include <iostream>
#include <algorithm>
using namespace std;

int n;
int a[1000010];
int sum = 1;

int main()
{
	// 输入
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	
	// 排序 + 贪心
	sort(a+1, a+n+1);
	for (int i = 1; i <= n; i++)
	{
		if (a[i] < sum) continue;
		else sum++;
	}
	
	// 输出
	cout << sum-1;
	return 0;
}

代码解析

  • 将题单的题目数量进行排序
  • 遍历题单:
    • 如果题单里的题目数量小于等于当前的天数:
      • 是:小杨同学可以完成这个题单
      • 否:小杨同学会偷懒
  • 最多偷懒的天数是指小杨同学可以不做题的最长连续天数,所以最后输出要 − 1 -1 1

[GESP202312 四级] 田忌赛马

题目描述

你要和田忌赛马。你们各自有 N N N 匹马,并且要进行 N N N 轮比赛,每轮比赛,你们都要各派出一匹马决出胜负。

你的马匹的速度分别为 u 1 , u 2 , ⋯ , u n u_1,u_2,\cdots,u_n u1,u2,un,田忌的马匹的速度分别为 v 1 , v 2 , ⋯   , v n v_1,v_2,\cdots,v_n v1,v2,,vn。田忌会按顺序派出他的马匹,请问你要如何排兵布阵,才能赢得最多轮次的比赛?巧合的是,你和田忌的所有马匹的速度两两不同,因此不可能出现平局。

输入格式

第一行一个整数 N N N。保证 1 ≤ N ≤ 5 × 1 0 4 1\le N \le 5\times 10^4 1N5×104

接下来一行 N N N 个用空格隔开的整数,依次为 u 1 , u 2 , ⋯   , u n u_1,u_2,\cdots,u_n u1,u2,,un,表示你的马匹们的速度。保证 1 ≤ u i ≤ 2 N 1\le u_i\le 2N 1ui2N

接下来一行 N N N 个用空格隔开的整数,依次为 v 1 , v 2 , ⋯   , v n v_1,v_2,\cdots,v_n v1,v2,,vn,表示田忌的马匹们的速度。保证 1 ≤ v i ≤ 2 N 1\le v_i\le 2N 1vi2N

输出格式

输出一行,表示你最多能获胜几轮。

样例 #1

样例输入 #1

3
1 3 5
2 4 6

样例输出 #1

2

样例 #2

样例输入 #2

5
10 3 5 8 7
4 6 1 2 9

样例输出 #2

5

提示

样例解释 1

第 1 轮,田忌派出速度为 2 的马匹,你可以派出速度为 3 的马匹迎战,本轮你获胜。

第 2 轮,田忌派出速度为 4 的马匹,你可以派出速度为 5 的马匹迎战,本轮你获胜。

第 3 轮,田忌派出速度为 6 的马匹,你可以派出速度为 1 的马匹迎战,本轮田忌获胜。

如此,你可以赢得 2 轮比赛。

参考答案

// horseRacing.cpp
// 双指针、贪心
#include <iostream>
#include <algorithm>
using namespace std;

int n; // 马匹数量
int ans; // 最多赢的轮数
int l = 1, r = 1; // 左右指针
int a[500005]; // 你的马匹速度
int b[500005]; // 田忌马匹速度

int main()
{
    // 输入
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> b[i];
    }

    // 排序
    sort(a+1, a+n+1);
    sort(b+1, b+n+1);

    // 贪心
    ans = n;
    while (l <= n && r <= n) // 保证指针在范围内
    {
        if (a[r] <= b[l]) // 你的马匹速度<=田忌的速度
        {
            r++; // 你的马匹指针右移
            ans--; // 胜场数减少
        }
        else // 你的马匹速度>田忌的速度
        {
            r++; // 你的马匹指针右移
            l++; // 田忌的马匹指针右移
        }
    }

    // 输出
    cout << ans;
    return 0;
}

[GESP202306 四级] 幸运数

题目描述

小明发明了一种 “幸运数”。一个正整数,其偶数位不变(个位为第 1 1 1 位,十位为第 2 2 2 位,以此类推),奇数位做如下变换:将数字乘以 7 7 7,如果不大于 9 9 9 则作为变换结果,否则把结果的各位数相加,如果结果不大于 9 9 9 则作为变换结果,否则(结果仍大于 9 9 9)继续把各位数相加,直到结果不大于 9 9 9,作为变换结果。变换结束后,把变换结果的各位数相加,如果得到的和是 8 8 8 的倍数,则称一开始的正整数为幸运数。

例如, 16347 16347 16347:第 1 1 1 位为 7 7 7,乘以 7 7 7 结果为 49 49 49,大于 9 9 9,各位数相加为 13 13 13,仍大于 9 9 9,继续各位数相加,最后结果为 4 4 4;第 3 3 3 位为 3 3 3,变换结果为 3 3 3;第 5 5 5 位为 1 1 1,变换结果为 7 7 7。最后变化结果为 76344 76344 76344,对于结果 76344 76344 76344 其各位数之和为 24 24 24,是 8 8 8 的倍数。因此 16347 16347 16347 是幸运数。

输入格式

输入第一行为正整数 N N N,表示有 N N N 个待判断的正整数。约定 1 ≤ N ≤ 20 1 \le N \le 20 1N20

从第 2 2 2 行开始的 N N N 行,每行一个正整数,为待判断的正整数。约定这些正整数小于 1 0 12 10^{12} 1012

输出格式

输出 N N N 行,对应 N N N 个正整数是否为幸运数,如是则输出 ‘T’,否则输出 ‘F’。

提示:不需要等到所有输入结束在依次输出,可以输入一个数就判断一个数并输出,再输入下一个数。

样例 #1

样例输入 #1

2
16347
76344

样例输出 #1

T
F

提示

算法核心思想:

  • 将正整数进行数位分离
  • 对于偶数位上的数字,保持不变。
  • 对于奇数位上的数字,将它 × 7 \times7 ×7,并将结果各位数相加,直到结果 ≤ 9 \le9 9
  • 判断和是否是 8 8 8 的倍数,如果是,a则为幸运数;否则,不是幸运数。

参考答案

#include <iostream>
using namespace std;

int T;
long long n;

int trans(long long k)
{
	k *= 7;
	while (k > 9)
	{
	    int ans = 0;
	    while (k)
	    {
	        ans += k % 10;
	        k /= 10;
	    }
	    k = ans;
	}
	return k;
}

bool judge(long long n)
{
	int ans = 0;
	for (int p = 1; n; n /= 10, ++p)
	{
		if (p & 1) ans += trans(n % 10);
		else ans += n % 10;
	}
	return !(ans % 8);
}

int main()
{
	cin >> T;
	for (int i = 1; i <= T; i++)
	{
		cin >> n;
		cout << (judge(n) ? 'T' : 'F') << endl;
	}
	return 0;
}

[GESP202306 四级] 图像压缩

题目描述

图像是由很多的像素点组成的。如果用 0 0 0 表示黑, 255 255 255 表示白, 0 0 0 255 255 255 之间的值代表不同程度的灰色,则可以用一个字节表达一个像素(取值范围为十进制 0-255、十六进制 00-FF)。这样的像素组成的图像,称为 256 256 256 级灰阶的灰度图像。

现在希望将 256 256 256 级灰阶的灰度图像压缩为 16 16 16 级灰阶,即每个像素的取值范围为十进制 0-15、十六进制 0-F。压缩规则为:统计出每种灰阶的数量,取数量最多的前 16 16 16 种灰阶(如某种灰阶的数量与另外一种灰阶的数量相同,则以灰阶值从小到大为序),分别编号 0-F(最多的编号为 0,以此类推)。其他灰阶转换到最近的 16 16 16 种灰阶之一,将某个点的灰阶值(灰度,而非次数)与 16 16 16 种灰阶中的一种相减,绝对值最小即为最近,如果绝对值相等,则编号较小的灰阶更近。

输入格式

输入第 1 1 1 行为一个正整数 n ( 10 ≤ n ≤ 20 ) n(10\le n \le 20) n(10n20),表示接下来有 n n n 行数据组成一副 256 256 256 级灰阶的灰度图像。

2 2 2 行开始的 n n n 行,每行为长度相等且为偶数的字符串,每两个字符用十六进制表示一个像素。约定输入的灰度图像至少有 16 16 16 种灰阶。约定每行最多 20 20 20 个像素。

输出格式

第一行输出压缩选定的 16 16 16 种灰阶的十六进制编码,共计 32 32 32 个字符。

第二行开始的 n n n 行,输出压缩后的图像,每个像素一位十六进制数表示压缩后的灰阶值。

样例 #1

样例输入 #1

10
00FFCFAB00FFAC09071B5CCFAB76
00AFCBAB11FFAB09981D34CFAF56
01BFCEAB00FFAC0907F25FCFBA65
10FBCBAB11FFAB09981DF4CFCA67
00FFCBFB00FFAC0907A25CCFFC76
00FFCBAB1CFFCB09FC1AC4CFCF67
01FCCBAB00FFAC0F071A54CFBA65
10EFCBAB11FFAB09981B34CFCF67
01FFCBAB00FFAC0F071054CFAC76
1000CBAB11FFAB0A981B84CFCF66

样例输出 #1

ABCFFF00CB09AC07101198011B6776FC
321032657CD10E
36409205ACC16D
B41032657FD16D
8F409205ACF14D
324F326570D1FE
3240C245FC411D
BF4032687CD16D
8F409205ACC11D
B240326878D16E
83409205ACE11D

提示

【样例 1 1 1 解释】

灰阶 ABCFFF 出现 14 14 14 次,00 出现 10 10 10 次,CB 出现
9 9 9 次,09 出现 7 7 7 次,AC 出现 6 6 6 次,07 出现 5 5 5 次,1011
98 出现 4 4 4 次,011B6776FC 出现 3 3 3 次。

算法核心思想:

  • 记录每种灰阶的出现次数
  • 灰阶按照出现次数从大到小排序,判断出现次数是否有相同:
    • 有则按照灰阶值从小到大排序
  • 取出前 16 16 16 个灰阶作为压缩选定的灰阶
  • 遍历压缩选定的灰阶
    • 计算每个压缩灰阶与当前像素的差值的绝对值,找到最小的差值对应的压缩灰阶
    • 将这个压缩灰阶的索引转换为十六进制表示,并输出到压缩后的图像中

参考答案

// 没有做出来真的不怪大家,题目太难了,而且是最恶心的模拟
#include <iostream>
#include <vector>
#include <algorithm>
const int N = 30;
using namespace std;

struct Node
{
	int tot, g;
	friend bool operator < (Node a, Node b)
	{
		return a.tot != b.tot ? a.tot > b.tot : a.g < b.g;
	}
};

int n;
Node a[300];
vector <pair <int, char> > mm;
int pic[100][100];

int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n;
	int m = n;
	int len;
	for (int i = 1; i <= n; i++)
	{
		string s;
		cin >> s;
		len = s.size();
		for (int j = 0; j < s.size(); j += 2)
		{
			int cnt = (s[j] > '9' ? s[j] - 'A' + 10 : s[j] - '0') * 16 + (s[j + 1] > '9' ? s[j + 1] - 'A' + 10 : s[j + 1] - '0');
			a[cnt].tot++, a[cnt].g = cnt;
			pic[i][j / 2 + 1] = cnt;
		}
	}
	
	sort(a, a+299);
	for (int i = 0; i < 16; i++)
	{
		cout << char(a[i].g / 16 > 9 ? a[i].g / 16 - 10 + 'A' : a[i].g / 16 + '0');
		cout << char(a[i].g % 16 > 9 ? a[i].g % 16 - 10 + 'A' : a[i].g % 16 + '0');
	}
	cout << endl;
	
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= len / 2; j++)
		{
			int mina = 300;
			char c;
			for (int k = 0; k < 16; k++)
			{
				if (abs(a[k].g - pic[i][j]) < mina)
				{
					mina = abs(a[k].g - pic[i][j]), c = (k > 9 ? k - 10 + 'A' : k + '0');
				}
			}
			cout << c;
		}
		cout << endl;
	}
	return 0;
}
  • 30
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值