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
题目分析:
- 审题后,我们可以看出,输入的链表同样可以表示为以下形式:
00100 1 12309
12309 2 33218
33218 3 00000
00000 4 99999
99999 5 68237
68237 6 -1
- 通过观察,我们可以发现,反转链表仅对结点1-4号做了处理,这说明我们需要统计需要反转的结点个数。我们仅对需要翻转的结点进行操作,其他结点保持不变。
- 在代码实现上,我们将结点的前地址、后地址分别保存在两个数组中,通过下标作为序号,一一对应。本题因为仅涉及到反转,我们可以直接采用数组的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个符号计算,每次向两边扩展2行,总共需要i * (i+2)个符号,向一个方向看,库扩展了i-1行,
- 在代码实现上,需要考虑多种情况:
- 扩展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
题目分析:
- 本题的难点在于
- 如何判断输入的字符已经在字符串中
- 如何进行大小写的转换
- 在算法思路上,我们定义一标准的字符串s1,一个输入的字符串s2,一个结果字符串s3,遍历s1,当s1[i]不在s2中,且s1[i]的大写形式也不再s2中时,证明这个字母在s2中无法找到,对应的键损坏,将此字母放入s3中,输出s3即可。
- 在代码的具体实现上,
- 我们可以采用字符串的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
题目分析:
- 审题后,我们可以发现,完美数列的定义为,设最大值为M,最小值为m,如果M <= m * p, 则称这个数列为完美数列。
- 题目的要求是,让我们选取尽可能多的数,那也就是在M和m之间插入尽可能多的值。
- 在算法思路上,我们先将数列的所有元素进行排序,然后再从排好序的数列中进行选择:
- 设置两个指针,指针j指向最大值,指针i指向最小值
- i从1,即从第一个元素开始计算,如果a[i]和a[i+1]满足M<= m*p,则将指向最大值的指针向后移动,以此类推。
- 在代码实现上,正整数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; }