PAT 7.8

这篇博客探讨了四个不同的编程题目,涉及链表操作和数据结构的应用。首先介绍了如何反转链表的每K个节点,然后展示了如何打印出沙漏形状的符号序列。接着是识别旧键盘上坏键的问题,最后讲解了如何从一组数字中选择元素构建完美数列。这些题目都涉及到对数据结构的深入理解和巧妙操作。
摘要由CSDN通过智能技术生成

PAT Day 6

致谢:本博客参考了 @柳婼 的PAT乙级题解,清晰的算法思路、简洁的代码给了我很大启发。

完成题目: 1025-1030

1025. 反转链表 (25)

题目描述:

给定⼀个常数K以及⼀个单链表L,请编写程序将L中每K个结点反转。

例如:给定L为1→2→3→4→5→6,K为3,则输出应该为3→2→1→6→5→4;

如果K为4,则输出应该为4→3→2→1→5→6,即最后不到K个元素不反转。

输⼊格式:

每个输⼊包含1个测试⽤例。每个测试⽤例第1⾏给出第1个结点的地址、结点总个数正整数N(<= 10^5)、以及正整数K(<=N),
即要求反转的⼦链结点的个数。结点的地址是5位⾮负整数,NULL地址⽤-1表示。
接下来有N⾏,每⾏格式为:

Address Data Next

其中Address是结点地址,Data是该结点保存的整数数据,Next是下⼀结点的地址。

输出格式:

对每个测试⽤例,顺序输出反转后的链表,其上每个结点占⼀⾏,格式与输⼊相同。

输⼊样例:
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
输出样例:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1

题目分析:

  1. 审题后,我们可以看出,输入的链表同样可以表示为以下形式:
00100 1 12309
12309 2 33218
33218 3 00000
00000 4 99999
99999 5 68237
68237 6 -1
  1. 通过观察,我们可以发现,反转链表仅对结点1-4号做了处理,这说明我们需要统计需要反转的结点个数。我们仅对需要翻转的结点进行操作,其他结点保持不变。
  2. 在代码实现上,我们将结点的前地址、后地址分别保存在两个数组中,通过下标作为序号,一一对应。本题因为仅涉及到反转,我们可以直接采用数组的reverse()方法。示例代码如下:
int first, k, n, temp;
 cin >> first >> n >> k;
 int data[100005], next[100005], list[100005];
 for (int i = 0; i < n; i++) {
 cin >> temp;
 cin >> data[temp] >> next[temp]; //输入链表
 }

 int sum = 0;
 while (first != -1) {
 list[sum++] = first;
 first = next[first];
 }
 for (int i = 0; i < (sum - sum % k); i += k)
 reverse(begin(list) + i, begin(list) + i + k); //反转结点
 for (int i = 0; i < sum - 1; i++)
 printf("%05d %d %05d\n", list[i], data[list[i]], list[i + 1]);
 printf("%05d %d -1", list[sum - 1], data[list[sum - 1]]);

1027. 打印沙漏(20)

题目描述:

本题要求你写个程序把给定的符号打印成沙漏的形状。

例如给定17个“*”,要求按下列格式打印

*****
 ***
  *
 ***
*****

所谓“沙漏形状”,是指每⾏输出奇数个符号;各⾏符号中⼼对⻬;

相邻两⾏符号数差2;符号数先从⼤到⼩顺序递减到1,再从⼩到⼤顺序递增;⾸尾符号数相等。

给定任意N个符号,不⼀定能正好组成⼀个沙漏。要求打印出的沙漏能⽤掉尽可能多的符号。

输⼊格式:

输⼊在⼀⾏给出1个正整数N(<=1000)和⼀个符号,中间以空格分隔。

输出格式:

⾸先打印出由给定符号组成的最⼤的沙漏形状,最后在⼀⾏中输出剩下没⽤掉的符号数。

输⼊样例:
19 *
输出样例:
*****
***
 *
***
*****
2

题目分析:

(本题的数学分析上借鉴了其他博主的思路)

  1. 对沙漏图形进行分析:自中间层需要1个符号计算,每次向两边扩展2行,总共需要i * (i+2)个符号,向一个方向看,库扩展了i-1行,
  2. 在代码实现上,需要考虑多种情况:
  • 扩展n行时,n = i - 1,对于扩展出去的第 i 层需要输出row – i个空格,接着输出i *2 + 1个符号c和换⾏符;
  • 对于最中间⼀⾏,需要输出n – 1个空格、符号c和换⾏符;
  • 对于下⾯的每⼀⾏,对于扩展出去的第i层,需要输出row-i个空格,接着输出i * 2 + 1个符号c和换⾏符。
  • 因为⽤掉的符号数为2 * row * (row + 2) + 1,所以最后输出剩下没⽤掉的符号数为N – (2 * row * (row + 2) + 1)。
  • 示例代码如下:
int N, row = 0;
 char c;
 cin >> N >> c;
 for (int i = 0; i < N; i++) {
 if ((2 * i * (i + 2) + 1) > N) {
 row = i - 1;
 break;
 }
 }
 //输出上半部分:
 for (int i = row; i >= 1; i--) {
 for (int k = row - i; k >= 1; k--) cout << " ";
 for (int j = i * 2 + 1; j >= 1; j--) cout << c;
 cout << endl;
 }
 // 输出中间部分:
 for (int i = 0; i < row; i++) cout << " ";
 cout << c << endl;
 // 输出下半部分:
 for (int i = 1; i <= row; i++) {
 for (int k = row - i; k >= 1; k--) cout << " ";
 for (int j = i * 2 + 1; j >= 1; j--) cout << c;
 cout << endl;
 }
 cout << (N - (2 * row * (row + 2) + 1));
 return 0;

1029. 旧键盘(20)

题目描述:

旧键盘上坏了⼏个键,于是在敲⼀段⽂字的时候,对应的字符就不会出现。现在给出应该输⼊的⼀段⽂字、以及实际被输⼊的⽂字,请你列出肯定坏掉的那些键。

输⼊格式:

输⼊在2⾏中分别给出应该输⼊的⽂字、以及实际被输⼊的⽂字。每段⽂字是不超过80个字符的串,由字⺟A-Z(包括⼤、⼩写)、数字0-9、以及下划线“_”(代表空格)组成。题⽬保证2个字符串均⾮空。

输出格式:

按照发现顺序,在⼀⾏中输出坏掉的键。其中英⽂字⺟只输出⼤写,每个坏键只输出⼀次。题⽬保证⾄少有1个坏键。

输⼊样例:
7_This_is_a_test
_hs_s_a_es
输出样例:
7TI

题目分析:

  1. 本题的难点在于
  • 如何判断输入的字符已经在字符串中
  • 如何进行大小写的转换
  1. 在算法思路上,我们定义一标准的字符串s1,一个输入的字符串s2,一个结果字符串s3,遍历s1,当s1[i]不在s2中,且s1[i]的大写形式也不再s2中时,证明这个字母在s2中无法找到,对应的键损坏,将此字母放入s3中,输出s3即可。
  2. 在代码的具体实现上,
  • 我们可以采用字符串的find()函数,来判断s1[i]是否在s2中,而不必再遍历s2的每个字符与s1[i]相比较。对于find()函数的结果,我们可以与string::npos想比较即可,这个语句表示字符串不存在的位置
  • 在大小写的转换上,我们可以采用toupper()函数,将s1[i]转换为大写的形式。示例代码如下:
string s1, s2, ans;
 cin >> s1 >> s2;
 for (int i = 0; i < s1.length(); i++)
 if (s2.find(s1[i]) == string::npos && ans.find(toupper(s1[i])) ==
string::npos) //如果没有找到
 ans += toupper(s1[i]);  // 转换为大写形式
 cout << ans;

1030. 完美数列(25)

题目描述:

给定⼀个正整数数列,和正整数p,设这个数列中的最⼤值是M,最⼩值是m,如果M <= m * p,则称这个数列是完美数列。

现在给定参数p和⼀些正整数,请你从中选择尽可能多的数构成⼀个完美数列。

输⼊格式:

输⼊第⼀⾏给出两个正整数N和p,其中N(<= 105)是输⼊的正整数的个数,p(<= 109)是给定的参数。第⼆⾏给出N个正整数,每个数不超过109。

输出格式:

在⼀⾏中输出最多可以选择多少个数可以⽤它们组成⼀个完美数列。

输⼊样例:
10 8
2 3 20 4 5 1 6 7 8 9
输出样例:
8

题目分析:

  1. 审题后,我们可以发现,完美数列的定义为,设最大值为M,最小值为m,如果M <= m * p, 则称这个数列为完美数列。
  2. 题目的要求是,让我们选取尽可能多的数,那也就是在M和m之间插入尽可能多的值。
  3. 在算法思路上,我们先将数列的所有元素进行排序,然后再从排好序的数列中进行选择:
  • 设置两个指针,指针j指向最大值,指针i指向最小值
  • i从1,即从第一个元素开始计算,如果a[i]和a[i+1]满足M<= m*p,则将指向最大值的指针向后移动,以此类推。
  1. 在代码实现上,正整数p我们可以采用long long类型,以确保输入的范围足够大,而对于数列,我们采用vector的形式,这样方便了插入n个元素,同时也可以利用sort()函数,对vector所有元素进行排序。示例代码如下:
int main() {
 int n;
 long long p;
 scanf("%d%lld", &n, &p);
 vector<int> v(n);
 for (int i = 0; i < n; i++)
 cin >> v[i];//输入n个值
 sort(v.begin(), v.end()); //有序化

 int result = 0, temp = 0; //设置两个指针
 for (int i = 0; i < n; i++) {
 for (int j = i + result; j < n; j++) {
 if (v[j] <= v[i] * p) { //如果满足完美数列的条件,移动最大值指针
 temp = j - i + 1;

 if (temp > result) 
 result = temp;
 } else {
 break;
}
 }
 }
 cout << result;
 return 0; } 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值