202306-1 幸运数
题目描述
小明发明了一种 “幸运数”。一个正整数,其偶数位不变(个位为第 位,十位为第 位,以此类推),奇数位做如下变换:将数字乘以 ,如果不大于 则作为变换结果,否则把结果的各位数相加,如果结果不大于 则作为变换结果,否则(结果仍大于 )继续把各位数相加,直到结果不大于 ,作为变换结果。变换结束后,把变换结果的各位数相加,如果得到的和是 的倍数,则称一开始的正整数为幸运数。
例如,:第 位为 ,乘以 结果为 ,大于 ,各位数相加为 ,仍大于 ,继续各位数相加,最后结果为 ;第 位为 ,变换结果为 ;第 位为 ,变换结果为 。最后变化结果为 ,对于结果 其各位数之和为 ,是 的倍数。因此 是幸运数。
输入格式
输入第一行为正整数 ,表示有 个待判断的正整数。约定 。
从第 行开始的 行,每行一个正整数,为待判断的正整数。约定这些正整数小于 。
输出格式
输出 行,对应 个正整数是否为幸运数,如是则输出 T,否则输出 F。
提示:不需要等到所有输入结束再依次输出,可以输入一个数就判断一个数并输出,再输入下一个数。
样例输入 1
21634776344样例输出 1
TF代码解析
模拟法,根据题目描述,模拟每个数的变换过程,判断是否为幸运数。
#include<bits/stdc++.h>using namespace std;
int f(int a) { int sum = 0; while (a) { sum += a % 10; a /= 10; } if (sum > 9) return f(sum); return sum;}
int main() {
int n; cin >> n; while (n--) { long long num, cnt = 0; cin >> num; bool flag = true; while (num) { int ans = num % 10; if (flag) { ans *= 7; ans = f(ans); } cnt += ans; num /= 10; flag = !flag; }
cout << (cnt % 8 == 0 ? 'T' : 'F') << endl; }
return 0;}202306-2 图像压缩
题目描述
图像是由很多的像素点组成的。如果用 表示黑, 表示白, 和 之间的值代表不同程度的灰色,则可以用一个字节表达一个像素(取值范围为十进制 0-255、十六进制 00-FF)。这样的像素组成的图像,称为 级灰阶的灰度图像。
现在希望将 级灰阶的灰度图像压缩为 级灰阶,即每个像素的取值范围为十进制 0-15、十六进制 0-F。压缩规则为:统计出每种灰阶的数量,取数量最多的前 种灰阶(如某种灰阶的数量与另外一种灰阶的数量相同,则以灰阶值从小到大为序),分别编号 0-F(最多的编号为 0,以此类推)。其他灰阶转换到最近的 种灰阶之一,将某个点的灰阶值(灰度,而非次数)与 种灰阶中的一种相减,绝对值最小即为最近,如果绝对值相等,则编号较小的灰阶更近。
输入格式
输入第 行为一个正整数 ,表示接下来有 行数据组成一副 级灰阶的灰度图像。
第 行开始的 行,每行为长度相等且为偶数的字符串,每两个字符用十六进制表示一个像素。约定输入的灰度图像至少有 种灰阶。约定每行最多 个像素。
输出格式
第一行输出压缩选定的 种灰阶的十六进制编码,共计 个字符。
第二行开始的 行,输出压缩后的图像,每个像素一位十六进制数表示压缩后的灰阶值。
样例输入 1
1000FFCFAB00FFAC09071B5CCFAB7600AFCBAB11FFAB09981D34CFAF5601BFCEAB00FFAC0907F25FCFBA6510FBCBAB11FFAB09981DF4CFCA6700FFCBFB00FFAC0907A25CCFFC7600FFCBAB1CFFCB09FC1AC4CFCF6701FCCBAB00FFAC0F071A54CFBA6510EFCBAB11FFAB09981B34CFCF6701FFCBAB00FFAC0F071054CFAC761000CBAB11FFAB0A981B84CFCF66样例输出 1
ABCFFF00CB09AC07101198011B6776FC321032657CD10E36409205ACC16DB41032657FD16D8F409205ACF14D324F326570D1FE3240C245FC411DBF4032687CD16D8F409205ACC11DB240326878D16E83409205ACE11D提示
【样例 解释】
灰阶 AB、CF 和 FF 出现 次,00 出现 次,CB 出现
次,09 出现 次,AC 出现 次,07 出现 次,10、11
和 98 出现 次,01、1B、67、76 和 FC 出现 次。
代码解析
先读懂题目,题目给出的数据每两位代表一个 16 进制数字,所以第一步处理,我们两两转换成 10 进制数,保存在
pic数组中,同时统计每个数字出现的次数,保存在cnt数组中。 压缩的规则是:出现最多次数最多的前 个数字,分别修改为0-F(也就是 0 到 15)。其他的数字需要在 个数字中找到差值最小的一个,转换为对应的编号。提供一个思路,把
0 ~ 255这 个数字按照数组cnt中统计的出现次数从大到小排序,保存在top_16数组中,数组中的值类似{235, 23, 43, 143...},下标就是他们的名次。 然后修改cnt数组,把top_16数组的下标和值反过来,保存在cnt数组中,这样就可以通过cnt[i]得到数字i对应的排名。 最后遍历pic数组,编写函数pic_to_char,根据pic[i][j]在cnt数组中的排名进行处理,如果排名在前 名,直接输出对应的十六进制字符;否则,找到排名前 名中与pic[i][j]差值最小的一个,输出对应的十六进制字符。
#include <bits/stdc++.h>using namespace std;int n, m = 0;int pic[25][25];int cnt[256];int top_16[256];int hex_to_int(char a, char b) { if (isdigit(a)) a -= '0'; else a = a - 'A' + 10; if (isdigit(b)) b -= '0'; else b = b - 'A' + 10; return a * 16 + b;}char int_to_char(int a) { if (a < 10) return a + '0'; return a - 10 + 'A';}string int_to_hex(int a) { string s; s += int_to_char(a / 16); s += int_to_char(a % 16); return s;}bool cmp(int a, int b) { if (cnt[a] != cnt[b]) return cnt[a] > cnt[b]; return a < b;}char pic_to_char(int a) { if (cnt[a] < 16) return int_to_char(cnt[a]); int ans = 256, idx = 0; // 打擂台找前16个数字的最小差值 for (int i = 0; i < 16; i++) if (abs(top_16[i] - a) < ans) { idx = i; ans = abs(top_16[i] - a); } return int_to_char(idx);}int main() {
string s; cin >> n; for (int i = 0; i < n; i++) { cin >> s; int num = 0; // 这里把每两个字符转成10进制保存起来 for (int j = 0; j < s.size(); j+=2) { pic[i][j/2] = hex_to_int(s[j], s[j+1]); cnt[pic[i][j/2]]++; } } m = s.size()/2; // 记录一下一行有几个16进制数字
// 填充 0 ~ 255 for (int i = 0; i < 256; i++) top_16[i] = i; // 按照cnt[i]中每个数字i出现的次数序 // 之后只采用前16名 sort(top_16, top_16+256, cmp); // 构造一个下标到排名的对应关系 for (int i = 0; i < 256; i++) cnt[top_16[i]] = i;
for (int i = 0; i < 16; i++) cout << int_to_hex(top_16[i]);
cout << endl; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) cout << pic_to_char(pic[i][j]); cout << endl; }
return 0;}202309-1 进制转换
题目描述
进制数指的是逢 进一的计数制。例如,人们日常生活中大多使用十进制计数,而计算机底层则一般使用二进制。除此之外,八进制和十六进制在一些场合也是常用的计数制(十六进制中,一般使用字母 A 至 F 表示十至十五;本题中,十一进制到十五进制也是类似的)。
在本题中,我们将给出 个不同进制的数。你需要分别把它们转换成十进制数。
输入格式
输入的第一行为一个十进制表示的整数 。接下来 行,每行一个整数 ,随后是一个空格,紧接着是一个 进制数,表示需要转换的数。保证所有 进制数均由数字和大写字母组成,且不以 开头。保证 进制数合法。
保证 ;保证 。
保证所有 进制数的位数不超过 。
输出格式
输出 行,每一个十进制数,表示对应 进制数的十进制数值。
样例输入 1
28 136216 3F0样例输出 1
7541008样例输入 2
22 1101110 123456789样例输出 2
27123456789提示
对于任意一个 位 进制数,假设其最右边的数位为第 位,最左边的数位为第 位,我们只需要将其第 位的数码乘以权值 ,再将每位的结果相加,即可得到原 进制数对应的十进制数。下面是两个例子:
-
八进制数
1362对应的十进制数为:; -
十六进制数
3F0对应的十进制数为:。
代码解析
课堂中讲过的原题,注意字符转数字的方法,还有输入的数可能会超过
int范围,需要用long long存储。
#include<bits/stdc++.h>using namespace std;int char_to_int(char c) { if ('0' <= c && c <= '9') return c - '0'; return c - 'A' + 10;}long long k_to_dec(int k, string s) { long long num = 0; for (int i = 0; i < s.size(); i++) { num *= k; num += char_to_int(s[i]); } return num;}int main() {
int n; cin >> n; while (n--) { int k; string s; cin >> k >> s; cout << k_to_dec(k, s) << endl; }
return 0;}202309-2 变长编码
题目描述
小明刚刚学习了三种整数编码方式:原码、反码、补码,并了解到计算机存储整数通常使用补码。但他总是觉得,生活中很少用到 这么大的数,生活中常用的 这种数也同样需要用 个字节的补码表示,太浪费了些。 热爱学习的小明通过搜索,发现了一种正整数的变长编码方式。这种编码方式的规则如下:
-
对于给定的正整数,首先将其表达为二进制形式。例如,,。
-
将二进制数从低位到高位切分成每组 bit,不足 bit 的在高位用 填补。例如, 变为 的一组, 变为 和 的两组。
-
由代表低位的组开始,为其加入最高位。如果这组是最后一组,则在最高位填上 ,否则在最高位填上 。于是, 的变长编码为 一个字节, 的变长编码为 和 两个字节。
这种编码方式可以用更少的字节表达比较小的数,也可以用很多的字节表达非常大的数。例如, 的二进制为 ,于是它的变长编码为(十六进制表示) CE 96 C8 A6 F4 CB B6 DA 0D,共 个字节。
你能通过编写程序,找到一个正整数的变长编码吗?
输入格式
输入第一行,包含一个正整数 。约定 。
输出格式
输出一行,输出 对应的变长编码的每个字节,每个字节均以 位十六进制表示(其中, A-F 使用大写字母表示),两个字节间以空格分隔。
样例输入 1
0样例输出 1
00样例输入 2
926样例输出 2
9E 07样例输入 3
987654321012345678样例输出 3
CE 96 C8 A6 F4 CB B6 DA 0D代码解析
将输入的正整数转换为二进制形式,然后从低位到高位切分成每组 bit,处理完成之后,将每组从二进制字符串转换为十六进制,最后输出。
#include<bits/stdc++.h>using namespace std;char int_to_char(int a);string dec_to_longbin(string s1);string longbin_to_hex(string s);int main() {
long long a; string s, s1, s2; cin >> a; // 用 do-while 循环,因为 a 可能为 0 do { s1 += int_to_char(a & 1); a = a >> 1; } while (a); // 末尾补一个空格,方便分割 s2 = dec_to_longbin(s1) + ' '; for (int i = 0; i < s2.size(); i++) { if (s2[i] != ' ') { s += s2[i]; } else { cout << longbin_to_hex(s) << ' '; s = ""; } }
return 0;}char int_to_char(int a) { if (a < 10) return a + '0'; return a - 10 + 'A';}string dec_to_longbin(string s1) { string s, s2; int cnt = 0; for (int i = 1; i < s1.size(); i++) { cnt++; s = s1[i-1] + s; if (i % 7 == 0) { s = '1' + s + ' '; s2 += s; s = ""; cnt = 0; } } s = s1[s1.size() - 1] + s; cnt++; for (int j = 0; j < 8-cnt; j++) s = '0' + s; return s2 += s;}string longbin_to_hex(string s) { string s1; int n = 0, m = 0; for (int i = 0; i < 4; i++) { n *= 2; n += s[i] - '0'; } s1 += int_to_char(n); for (int i = 4; i < 8; i++) { m *= 2; m += s[i] - '0'; } s1 += int_to_char(m); return s1;}202312-1 小杨的字典
题目描述
在遥远的星球,有两个国家 A 国和 B 国,他们使用着不同的语言:A 语言和 B 语言。小杨是 B 国的翻译官,他的工作是将 A 语言的文章翻译成 B 语言的文章。
为了顺利完成工作,小杨制作了一本字典,里面记录了 个 A 语言单词对应的 B 语言单词,巧合的是,这些单词都 由地球上的 26 个小写英文字母组成。
小杨希望你写一个程序,帮助他根据这本字典翻译一段 A 语言文章。这段文章由标点符号 !()-.[].{}\|;:'",./?<> 和一些 A 语言单词构成,每个单词之间必定由至少一个标点符号分割,你的程序需要把这段话中的所有 A 语言单词替换成它的 B 语言翻译。特别地,如果遇到不在字典中的单词,请使用大写 UNK 来替换它。
例如,小杨的字典中包含 个 A 语言单词 abc 和 d,它们的 B 语言翻译分别为 a 和 def,那么我们可以把 A 语言文章 abc.d.d.abc.abcd. 翻译成 B 语言文章 a.def.def.a.UNK. 其中,单词 abcd 不在词典内,因此我们需要使用 UNK 来替换它。
输入格式
第一行一个整数 ,表示词典中的条目数。保证 。
接下来 行,每行两个用单个空格隔开的字符串 , ,分别表示字典中的一个 A 语言单词以及它对应的 B 语言翻译。保证所有 不重复;保证 和 的长度不超过 。
最后一行一个字符串 ,表示需要翻译的 A 语言文章。保证字符串 的长度不超过 ,保证字符串 只包含小写字母以及标点符号 !()-.[].{}\|;:'",./?<> 。
输出格式
输出一行,表示翻译后的结果。
样例输入 1
2abc ad defabc.d.d.abc.abcd样例输出 1
a.def.def.a.UNK样例输入 2
3abc ad defabcd xxxxabc,(d)d!-abc?abcd样例输出 2
a,(def)def!-a?xxxx样例输入 3
1abcdefghij klmnopqrst!()-[]{}\|;:'",./?<>abcdefghijklmnopqrstuvwxyz样例输出 3
!()-[]{}\|;:'",./?<>UNK代码解析
这道题是一个简单的字符串处理问题。我们可以用一个结构体数组来存储字典中的每个条目,每个条目包含一个 A 语言单词和它对应的 B 语言翻译。然后,我们可以遍历输入的字符串,每次遇到一个单词,就遍历数组,找到它对应的 B 语言翻译,替换成它。如果没有找到,就用 UNK 替换它。
#include<bits/stdc++.h>using namespace std;struct dict { string a, b;} d[100];int N;string str, word;bool is_flag(char c) { string s = "!()-.[].{}\\|;:\'\",./?<>"; for (int i = 0; i < s.size(); i++) { if (c == s[i]) return true; } return false;}string find(string s) { if (s == "") return s; for (int i = 0; i < N; i++) { if (s == d[i].a) return d[i].b; } return "UNK";}int main() {
cin >> N; for (int i = 0; i < N; i++) { cin >> d[i].a >> d[i].b; } cin >> str; str += '.'; for (int i = 0; i < str.size(); i++) { if (is_flag(str[i])) { cout << find(word); if (i < str.size() - 1) cout << str[i]; word = ""; } else { word += str[i]; } }
return 0;}202312-2 田忌赛马
题目描述
你要和田忌赛马。你们各自有 匹马,并且要进行 轮比赛,每轮比赛,你们都要各派出一匹马决出胜负。
你的马匹的速度分别为 ,田忌的马匹的速度分别为 。田忌会按顺序派出他的马匹,请问你要如何排兵布阵,才能赢得最多轮次的比赛?巧合的是,你和田忌的所有马匹的速度两两不同,因此不可能出现平局。
输入格式
第一行一个整数 。保证
接下来一行 个用空格隔开的整数,依次为 ,表示你的马匹们的速度。保证 。
接下来一行 个用空格隔开的整数,依次为 ,表示田忌的马匹们的速度。保证 。
输出格式
输出一行,表示你最多能获胜几轮。
样例输入 1
31 3 52 4 6样例输出 1
2样例输入 2
510 3 5 8 74 6 1 2 9样例输出 2
5提示
样例解释 1
第 1 轮,田忌派出速度为 2 的马匹,你可以派出速度为 3 的马匹迎战,本轮你获胜。
第 2 轮,田忌派出速度为 4 的马匹,你可以派出速度为 5 的马匹迎战,本轮你获胜。
第 3 轮,田忌派出速度为 6 的马匹,你可以派出速度为 1 的马匹迎战,本轮田忌获胜。
如此,你可以赢得 2 轮比赛。
代码解析
双指针的思想,因为我们处于上帝视角,所以可以先对我们和田忌的马匹进行排序,然后用两个下标
i和j,分别指向我们和田忌的第 1 匹马,每次比较我们的第i匹马和田忌的第j匹,如果我们的第i匹马快于田忌的第j匹,那么我们就可以用我们的第i匹马迎战田忌的第j匹,否则,我们就需要用我们的下一匹马继续试,只有我们赢的时候,j才会增加,所以j也就是我们的胜利轮数。
#include<bits/stdc++.h>using namespace std;int main() {
int a[50005], b[50005], n; cin >> n; for (int i = 0; i < n; i++) cin >> a[i]; for (int i = 0; i < n; i++) cin >> b[i]; sort(a, a+n); sort(b, b+n); int i = 0, j = 0, cnt = 0; while (i < n && j < n) { // 如果我们的第 `i` 匹马慢于田忌第 `j` 匹,则拿我们下一匹马继续试 if (a[i] <= b[j]) { i++; } else { i++, j++; } } cout << j;
return 0;}
陕公网安备61010302001363号