diff --git a/_posts/2018-05-03-a1001.md b/_posts/2018-05-03-a1001.md new file mode 100644 index 0000000..ee9d678 --- /dev/null +++ b/_posts/2018-05-03-a1001.md @@ -0,0 +1,80 @@ +--- +layout: post +title: "1001. A+B Format (20)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

Calculate a + b and output the sum in standard format -- that is, the digits must be separated into groups of three by commas (unless there are less than four digits).

+>

Input

+>

Each input file contains one test case. Each case contains a pair of integers a and b where -1000000 <= a, b <= 1000000. The numbers are separated by a space.

+>

Output

+>

For each test case, you should output the sum of a and b in one line. The sum must be written in the standard format.

+> Sample Input
+> -1000000 9
+> 
+> Sample Output
+> -999,991
+> 
+>

+ +## 思路 + +**2018/1/5更新0:重要参考链接** +Stack overflow有[一个帖子](https://stackoverflow.com/questions/1449805/how-to-format-a-number-from-1123456789-to-1-123-456-789-in-c)专门讨论如何做这个事情,我自己想到的三个也都是问题里几个高票答案的方法,大家可以学习一下。 + +两个要点: +- 两数和为0时要输出0; +- 注意逗号的输出位置,如不要在数字前面和后面有输出 + +2018/1/5更新1: + +又看了一遍题,总感觉之前的代码太繁琐,就改了一个方法,使用字符串处理。 +得到a和b后,将两者之和输入到一个字符串中去,使用`sprintf`函数。 +只需要从后向前,每三个数字判断一下是否需要加逗号即可。 + +2018/1/5更新2: + +还有一种更省事的方法,甚至C语言里都有直接的方法处理这种事情。那就是头文件,这个头文件可以处理不同地区或者语言的输出方式,其中就有对数字的处理。具体细节可查看相关资料,代码放在下面 +```c +#include +#include + +int main(void) +{ + setlocale(LC_NUMERIC, ""); + printf("%'d\n", 1123456789); + return 0; +} +``` + +[更新前的代码](https://github.com/OliverLew/PAT/blob/8c2a69d350e254a03397227f1a7b7e28097a9ed3/PATAdvanced/1001.c) + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1001.c),欢迎交流 +```c +#include +#include + +int main() +{ + int a, b, pos; + char num[11]; + + scanf("%d%d", &a, &b); + sprintf(num, "%d", a + b); + + for(pos = strlen(num) - 3; pos > 0 && num[pos - 1] != '-'; pos -= 3) + { + memmove(num + pos + 1, num + pos, strlen(num) - pos + 1); + num[pos] = ','; + } + + puts(num); + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1002.md b/_posts/2018-05-03-a1002.md new file mode 100644 index 0000000..f4288bc --- /dev/null +++ b/_posts/2018-05-03-a1002.md @@ -0,0 +1,88 @@ +--- +layout: post +title: "1002. A+B for Polynomials (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

This time, you are supposed to find A+B where A and B are two polynomials.

+>

Input

+>

Each input file contains one test case. Each case occupies 2 lines, and each line contains the information of a polynomial: +> K N1 aN1 N2 aN2 ... NK aNK, where K is the number of nonzero terms in the polynomial, Ni and aNi (i=1, 2, ..., K) are the exponents and coefficients, respectively. It is given that 1 <= K <= 10,0 <= NK < ... < N2 < N1 <=1000. +>

+>

Output

+>

For each test case you should output the sum of A and B in one line, with the same format as the input. Notice that there must be NO extra space at the end of each line. Please be accurate to 1 decimal place.

+> Sample Input
+> 2 1 2.4 0 3.2
+> 2 2 1.5 1 0.5
+> 
+> Sample Output
+> 3 2 1.5 1 2.9 0 3.2
+> 
+>

+ +## 思路 + +最初由于数学概念的理解错误,纠结了半天。**0是零多项式,在题中将之归于“0项”多项式**。当然了,实际是没有0项多项式的,只有零多项式,但是非要输出个结果,0还是合理的。我一开始把0当成只有常数项的一项多项式,结果最后一测试点老过不去。。。。 + +很多人都用1000长度的数组存储多项式,确实思路简单,且并不是很浪费空间。我用了另一种思路,用数组模仿链表的实现。将A、B两多项式由次数从高到低依次计算,存入新数组。 + +**疑问**:最初最后一个测试点没过的时候,我以为又是小负数近似格式的问题(见[PAT Basic 1051. 复数乘法 (15)(C语言实现)](http://www.jianshu.com/p/7ac2e9cfa797)),便将系数绝对值小于0.05的项全忽略了。当然这么做也通过了,可能输入保证了精度也只有1位小数,这样就没有这个必要了。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1002.c),欢迎交流 +```c +#include +#include + +typedef struct Poly{ + double coef; + int exp; +} Poly[20]; + +int main() +{ + int KA, KB, Ksum = 0; + Poly A, B, sum; + + scanf("%d", &KA); + for(int i = 0; i < KA; i++) + scanf("%d %lf", &A[i].exp, &A[i].coef); + + scanf("%d", &KB); + for(int i = 0; i < KB; i++) + scanf("%d %lf", &B[i].exp, &B[i].coef); + + int i = 0, j = 0; + while(i < KA || j < KB) + { + if(i == KA || (j < KB && A[i].exp < B[j].exp)) + { + sum[Ksum].exp = B[j].exp; + sum[Ksum].coef = B[j++].coef; + } + else if(j == KB || (i < KA && A[i].exp > B[j].exp)) + { + sum[Ksum].exp = A[i].exp; + sum[Ksum].coef = A[i++].coef; + } + else + { + sum[Ksum].exp = A[i].exp; + sum[Ksum].coef = A[i++].coef + B[j++].coef; + } + if(fabs(sum[Ksum].coef) >= 0.05) Ksum++; + } + + printf("%d", Ksum); + + for(int i = 0; i < Ksum; i++) + printf(" %d %.1lf", sum[i].exp, sum[i].coef); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1003.md b/_posts/2018-05-03-a1003.md new file mode 100644 index 0000000..22f430d --- /dev/null +++ b/_posts/2018-05-03-a1003.md @@ -0,0 +1,179 @@ +--- +layout: post +title: "1003. Emergency (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

+>

Input

+>

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (<= 500) - the number of cities (and the cities are numbered from 0 to N-1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. +> It is guaranteed that there exists at least one path from C1 to C2. +>

+>

Output

+>

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather.
+> All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

+> Sample Input
+> 5 6 0 2
+> 1 2 1 5 3
+> 0 1 1
+> 0 2 2
+> 0 3 1
+> 1 2 1
+> 2 4 1
+> 3 4 1
+> 
+> Sample Output
+> 2 4
+> 
+>

+ +## 思路 + +我由于时间充裕,就不慌不忙地(OS:花了好长时间,`~~~~ (>_<) ~~~~`)写了个指针实现的双向图+Dijkstra算法。 +对,一开始一直不对找不到原因,就是因为做成了单向图,结果错好多。 + +重点是遍历算法,在遍历时顺便计算抢救队的数目,其实也没什么难度。 + + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1003.c),欢迎交流 +```c +#include +#include +#include + +#define Inf INT_MAX + +typedef struct Vertex *Vertex, *Vertexes; +typedef struct Adj *Adj, *Adjs; +typedef struct Graph *Graph; + +struct Vertex{ + int id; /* Unique id of a vertex */ + int known; /* If the vertex has been traversed */ + int dist; /* the distance along the path from start point */ + int nrescue; /* Rescue teams in this city */ + int totrescue; /* Total rescue teams along the path */ + int npath; /* Length of the path */ + Adj adj; /* Pointer to the next vertex */ +}; + +struct Adj{ + int id; /* The city's id it is connected to */ + int length; /* The length of the edge */ + Adj iter; /* Pointer to the next adjacent city */ +}; + +struct Graph{ + Vertexes vs; + Adjs es; + int nvertex; + int nadj; +}; + +/* Read the graph */ +void Read(Graph G) +{ + int nrescue; + for(int i = 0; i < G->nvertex; i++) + { + Vertex v = G->vs + i; + scanf("%d", &nrescue); + v->id = i; + v->known = 0; + v->dist = Inf; + v->nrescue = nrescue; + v->totrescue = nrescue; + v->npath = 0; + v->adj = NULL; + } + + int id1, id2, length; + for(int i = 0; i < G->nadj; i++) + { + scanf("%d %d %d", &id1, &id2, &length); + /* From id1 to id2 */ + Adj e = G->es + i; + e->id = id2; + e->length = length; + e->iter = G->vs[id1].adj; + G->vs[id1].adj = e; + /* The other direction */ + e++, i++; + e->id = id1; + e->length = length; + e->iter = G->vs[id2].adj; + G->vs[id2].adj = e; + } +} + +/* Find the shortest path length using Dijkstra alg, + * in the same time record the number of shortest paths and max rescue teams */ +void ModifiedDijkstra(Graph G) +{ + int minUnknownDist; + Vertex v, w; + while(1) + { + /* find the smallest unknown distance vertex */ + v = NULL; + minUnknownDist = Inf; + for(w = G->vs; w < &G->vs[G->nvertex]; w++) + if(!w->known && w->dist < minUnknownDist) + { + minUnknownDist = w->dist; + v = w; + } + if(v == NULL) break; + + v->known = 1; + for(Adj e = v->adj; e; e = e->iter) if(!G->vs[e->id].known) + { + w = G->vs + e->id; /* w is every adjacent vertex to v */ + /* find shorter distance */ + if(v->dist + e->length < w->dist) + { + w->npath = v->npath; + w->totrescue = w->nrescue + v->totrescue; + w->dist = v->dist + e->length; + } + /* find same shortest distance */ + else if(v->dist + e->length == w->dist) + { + w->npath += v->npath; + if(w->totrescue < w->nrescue + v->totrescue) + w->totrescue = w->nrescue + v->totrescue; + } + } + } +} + +int main() +{ + int N, M, C1, C2; + scanf("%d %d %d %d", &N, &M, &C1, &C2); + + /* Create graph */ + Vertexes vs = (Vertexes)malloc(N * sizeof(struct Vertex)); + Adjs es = (Adjs)malloc(M * 2 * sizeof(struct Adj)); + struct Graph sG = {.vs = vs, .es = es, .nvertex = N, .nadj = M * 2}; + Graph G = &sG; + + /* Read all the data and build the graph */ + Read(G); + G->vs[C1].dist = 0; + G->vs[C1].npath = 1; + + /* Find the shortest path and maximum rescue teams */ + ModifiedDijkstra(G); + + printf("%d %d", G->vs[C2].npath, G->vs[C2].totrescue); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1004.md b/_posts/2018-05-03-a1004.md new file mode 100644 index 0000000..a7d6873 --- /dev/null +++ b/_posts/2018-05-03-a1004.md @@ -0,0 +1,115 @@ +--- +layout: post +title: "1004. Counting Leaves (30)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+> A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child.

+>

Input

+>

Each input file contains one test case. Each case starts with a line containing 0 < N < 100, the number of nodes in a tree, and M (< N), the number of non-leaf nodes. Then M lines follow, each in the format:

+>
+> ID K ID[1] ID[2] ... ID[K]
+> 
+> where ID is a two-digit number representing a given non-leaf node, K is the number of its children, followed by a sequence of two-digit ID's of its children. For the sake of simplicity, let us fix the root ID to be 01.

+>

Output

+>

For each test case, you are supposed to count those family members who have no child for every seniority level starting from the root. The numbers must be printed in a line, separated by a space, and there must be no extra space at the end of each line.

+>

The sample case represents a tree with only 2 nodes, where 01 is the root and 02 is its only child. Hence on the root 01 level, there is 0 leaf node; and on the next level, there is 1 leaf node. Then we should output "0 1" in a line.

+> Sample Input
+> 2 1
+> 01 1 02
+> 
+> Sample Output
+> 0 1
+> 
+>
+ +## 思路 + +虽然是30分的题,但是好像比1003还简单。 + +同样使用了邻接链表(adjacent list),数据结构如下: +- Member(Vertex):表示一个家庭成员,我使用了两个结构成员: + - level,表示这个人在家谱中的辈分,最高的辈分是0,辈分越低,level越大 + - child,指向Child结构变量,即链表的第一个节点 +- Child(Edge):表示亲子关系,也使用了两个结构成员: + - ID,表示这个孩子的ID + - iter,指向相同父母的下一个孩子Child变量 + +读取数据,建立邻接链表。初始化01节点的level为0,其他level为INF。 + +然后按level从0开始,依次遍历所有Member,找出相应辈分的家庭成员。这时分两种情况:这个成员没有孩子,那么需要进行计数;这个成员有孩子,那么需要更新他们的level值为这个成员的level+1,以便下一次遍历时找到他们。 + +使用一个标记表明何时不需再遍历,我用的是记录人数,当之前所有level的人数已经是总人数,那么说明已全部遍历,此时退出循环。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1004.c),欢迎交流 +```c +#include + +#define MAX 999 + +typedef struct Member *Member; +typedef struct Child *Child; + +struct Member{ + int level; + Child child; +}; + +struct Child{ + int ID; + Child iter; +}; + +int main() +{ + int N, M, ID, cID, K; + struct Member nodes[100]; + struct Child children[100]; + + /* Read data and initiate a adjacent list */ + scanf("%d %d", &N, &M); + for(int i = 1; i <= N; i++) + { + nodes[i].level = MAX; + nodes[i].child = NULL; + } + nodes[1].level = 0; /* root node at level 0 */ + for(int i = 0, k = 0; i < M; i++) + { + scanf("%d %d", &ID, &K); + for(; K--; k++) + { + scanf("%d", &cID); + children[k].ID = cID; + children[k].iter = nodes[ID].child; + nodes[ID].child = &children[k]; + } + } + + /* For every level, find leaf nodes */ + int n = N, count; + for(int level = 0; n; level++) + { + count = 0; + for(int i = 1; i <= N; i++) if(nodes[i].level == level) + { + n--; + if(nodes[i].child == NULL) + count++; + /* set the children to next level */ + for(Child c = nodes[i].child; c; c = c->iter) + nodes[c->ID].level = level + 1; + } + + printf("%d%c", count, n ? ' ' : '\0'); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1005.md b/_posts/2018-05-03-a1005.md new file mode 100644 index 0000000..a012387 --- /dev/null +++ b/_posts/2018-05-03-a1005.md @@ -0,0 +1,60 @@ +--- +layout: post +title: "1005. Spell It Right (20)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

Given a non-negative integer N, your task is to compute the sum of all the digits of N, and output every digit of the sum in English. +>

+>

+> Input Specification: +>

+>

Each input file contains one test case. Each case occupies one line which contains an N (<= 10100). +>

+>

+> Output Specification: +>

+>

For each test case, output in one line the digits of the sum in English words. There must be one space between two consecutive words, but no extra space at the end of a line. +>

+> Sample Input:
+> 12345
+> 
+> Sample Output:
+> one five
+> 
+>
+ +## 思路 + +和[PAT Basic 1002. 写出这个数 (20)(C语言实现)](http://www.jianshu.com/p/213843722abb)几乎是一样的,只不过将拼音变成了英语。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1005.c),欢迎交流 +```c +#include + +int main() +{ + int sum = 0; + char c, *digits[] = { + "zero", "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine" + }; + + while((c = getchar()) != '\n') + sum += c - '0'; + + if(sum >= 100) + printf("%s ", digits[sum / 100]); + if(sum >= 10) + printf("%s ", digits[sum % 100 / 10]); + printf("%s", digits[sum % 10]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1006.md b/_posts/2018-05-03-a1006.md new file mode 100644 index 0000000..3b4495a --- /dev/null +++ b/_posts/2018-05-03-a1006.md @@ -0,0 +1,81 @@ +--- +layout: post +title: "1006. Sign In and Sign Out (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

At the beginning of every day, the first person who signs in the computer room will unlock the door, and the last one who signs out will lock the door. Given the records of signing in's and out's, you are supposed to find the ones who have unlocked and locked the door on that day. +>

+>

+> Input Specification: +>

+>

Each input file contains one test case. Each case contains the records for one day. The case starts with a positive integer M, which is the total number of records, followed by M lines, each in the format:

+>
+> ID_number Sign_in_time Sign_out_time
+> 
+>

where times are given in the format HH:MM:SS, and ID number is a string with no more than 15 characters. +>

+>

+> Output Specification: +>

+>

For each test case, output in one line the ID numbers of the persons who have unlocked and locked the door on that day. The two ID numbers must be separated by one space.

+>

Note: It is guaranteed that the records are consistent. That is, the sign in time must be earlier than the sign out time for each person, and there are no two persons sign in or out at the same moment.

+> Sample Input:
+> 3
+> CS301111 15:30:28 17:00:10
+> SC3021234 08:00:00 11:25:25
+> CS301133 21:45:00 21:58:40
+> 
+> Sample Output:
+> SC3021234 CS301133
+> 
+>
+ +## 思路 + +基本和[PAT Basic 1028. 人口普查(20)(C语言实现)](http://www.jianshu.com/p/87656a0e7ef7)思路一致。 + +找到最早签到和最晚签出的即可。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1006.c),欢迎交流 +```c +#include +#include + +int main() +{ + int N, HH, MM, SS; + int firsttime = 86400, lasttime = -1, time; + char firstname[15], lastname[15], name[15]; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%s %d:%d:%d", name, &HH, &MM, &SS); + time = (HH * 24 + MM) * 60 + SS; + if(time < firsttime) + { + firsttime = time; + strcpy(firstname, name); + } + + scanf("%d:%d:%d", &HH, &MM, &SS); + time = (HH * 24 + MM) * 60 + SS; + if(time > lasttime) + { + lasttime = time; + strcpy(lastname, name); + } + } + + printf("%s %s", firstname, lastname); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1007.md b/_posts/2018-05-03-a1007.md new file mode 100644 index 0000000..6f9edd6 --- /dev/null +++ b/_posts/2018-05-03-a1007.md @@ -0,0 +1,79 @@ +--- +layout: post +title: "1007. Maximum Subsequence Sum (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

Given a sequence of K integers { N1, N2, ..., NK }. A continuous subsequence is defined to be { Ni, Ni+1, ..., Nj } where 1 <= i <= j <= K. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, given sequence { -2, 11, -4, 13, -5, -2 }, its maximum subsequence is { 11, -4, 13 } with the largest sum being 20.

+>

Now you are supposed to find the largest sum, together with the first and the last numbers of the maximum subsequence. +>

+>

+> Input Specification: +>

+>

Each input file contains one test case. Each case occupies two lines. The first line contains a positive integer K (<= 10000). The second line contains K numbers, separated by a space. +>

+>

+> Output Specification: +>

+>

For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line. In case that the maximum subsequence is not unique, output the one with the smallest indices i and j (as shown by the sample case). If all the K numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence. +>

+> Sample Input:
+> 10
+> -10 1 2 3 4 -5 -23 3 7 -21
+> 
+> Sample Output:
+> 10 1 4
+> 
+>
+ +## 思路 + +可以说是经典算法题目之一了,时间复杂度从O(N^3)到O(N)的算法都存在,编程珠玑、算法导论、数据结构和算法分析(C语言描述)这些书里都有谈到。 + +当然了这些书里的算法只管计算最大子列和(确实这样就太简单了),并没有记录相应头尾元素,还有什么全是负数还要输出整个数列首位元素(这就是为了出题而出题嘛)。但是程序的骨架是不变的。 + +O(N)复杂度的算法思路是:记录当前非负子列和和目前最大子列和,若前者变成了负数,便将其重置,直到遍历整个数组。这道题有一个要求: +> In case that the maximum subsequence is not unique, output the one with the smallest indices i and j + +需要索引最小的子列,这就要在判断条件上有精细的控制。要使得开头索引最小,就要将之前可能存在子列和为0的包含进来,而结尾索引也要最小,就要将之后可能存在子列和为0的排除,如: +2 -2 3 4 -5 5 +要选取前4个,而不能包括后2个。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1007.c),欢迎交流 +```c +#include + +int main() +{ + int K, n, first; + int start, end, sum = -1; /* the current non-negative subsequence */ + int a = -1, b, s = 0; /* the maximum-sum subsequence */ + + scanf("%d", &K); + for(int i = 0; i < K; i++) + { + scanf("%d", &n); + if(i == 0) first = n; /* record the first number */ + + /* update start, end and sum for the current non-negative subsequence */ + if(sum < 0) start = n, sum = 0; /* reset if sum < 0 */ + sum += n; /* update sum */ + if(sum >= 0) end = n; /* update end if sum >= 0 */ + + /* update a, b and s for the so-far maximum-sum subsequence */ + if(sum > s || (!sum && !s)) /* special case is max-sum is 0 */ + a = start, b = end, s = sum; + } + /* a won't be updated if all the numbers are negative */ + if(a == -1) printf("0 %d %d", first, n); + else printf("%d %d %d", s, a, b); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1008.md b/_posts/2018-05-03-a1008.md new file mode 100644 index 0000000..c4245ac --- /dev/null +++ b/_posts/2018-05-03-a1008.md @@ -0,0 +1,54 @@ +--- +layout: post +title: "1008. Elevator (20)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

The highest building in our city has only one elevator. A request list is made up with N positive numbers. The numbers denote at which floors the elevator will stop, in specified order. It costs 6 seconds to move the elevator up one floor, and 4 seconds to move down one floor. The elevator will stay for 5 seconds at each stop.

+>

For a given request list, you are to compute the total time spent to fulfill the requests on the list. The elevator is on the 0th floor at the beginning and does not have to return to the ground floor when the requests are fulfilled.

+>

+> Input Specification: +>

+>

Each input file contains one test case. Each case contains a positive integer N, followed by N positive numbers. All the numbers in the input are less than 100.

+>

+> Output Specification: +>

+>

For each test case, print the total time on a single line.

+> Sample Input:
+> 3 2 3 1
+> 
+> Sample Output:
+> 41
+> 
+>
+ +## 思路 + +很简单的题目,注意最后停住了5秒的等待时间也要算在内(其实不注意反倒不会有问题)。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1008.c),欢迎交流 +```c +#include +int main() +{ + int N, time, cur, pre; + + scanf("%d", &N); + for(pre = 0, time = 0; N--; pre = cur) + { + scanf("%d", &cur); + if(cur > pre) time += (cur - pre) * 6 + 5; + else time += (pre - cur) * 4 + 5; + } + + printf("%d", time); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1009.md b/_posts/2018-05-03-a1009.md new file mode 100644 index 0000000..18daaf0 --- /dev/null +++ b/_posts/2018-05-03-a1009.md @@ -0,0 +1,63 @@ +--- +layout: post +title: "1009. Product of Polynomials (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

This time, you are supposed to find A*B where A and B are two polynomials.

+>

+> Input Specification: +>

+>

Each input file contains one test case. Each case occupies 2 lines, and each line contains the information of a polynomial: +> K N1 aN1 N2 aN2 ... NK aNK, where K is the number of nonzero terms in the polynomial, Ni and aNi (i=1, 2, ..., K) are the exponents and coefficients, respectively. It is given that 1 <= K <= 10, 0 <= NK < ... < N2 < N1 <=1000. +>

+>

+> Output Specification: +>

+>

For each test case you should output the product of A and B in one line, with the same format as the input. Notice that there must be NO extra space at the end of each line. Please be accurate up to 1 decimal place.

+> Sample Input
+> 2 1 2.4 0 3.2
+> 2 2 1.5 1 0.5
+> 
+> Sample Output
+> 3 3 3.6 2 6.0 1 1.6
+> 
+>

+ +## 思路 + +为了思路简单,就直接开大数组遍历了,没有什么难点 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1009.c),欢迎交流 +```c +#include +int main() +{ + int N, exp, count = 0; + float coef, A[1001] = {0}, B[1001] = {0}, MUL[2001] = {0}; + + scanf("%d", &N); + while(N--){ scanf("%d %f", &exp, &coef); A[exp] = coef; } + scanf("%d", &N); + while(N--){ scanf("%d %f", &exp, &coef); B[exp] = coef; } + + for(int i = 0; i < 1001; i++) + for(int j = 0; j < 1001; j++) + MUL[i + j] += A[i] * B[j]; + + for(int i = 0; i < 2001; i++) + if(MUL[i]) count++; + + printf("%d", count); + for(int i = 2000; i >= 0; i--) if(MUL[i]) + printf(" %d %.1f", i, MUL[i]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1010.md b/_posts/2018-05-03-a1010.md new file mode 100644 index 0000000..e2dfd7b --- /dev/null +++ b/_posts/2018-05-03-a1010.md @@ -0,0 +1,136 @@ +--- +layout: post +title: "1010. Radix (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The answer is "yes", if 6 is a decimal number and 110 is a binary number. +>

+>

+> Now for any pair of positive integers N1 and N2, your task is to find the radix of one number while that of the other is given.

+>

+> Input Specification: +>

+>

Each input file contains one test case. Each case occupies a line which contains 4 positive integers:
+> N1 N2 tag radix
+> Here N1 and N2 each has no more than 10 digits. A digit is less than its radix and is chosen from the set {0-9, a-z} where 0-9 represent the decimal numbers 0-9, and a-z represent the decimal numbers 10-35. The last number "radix" is the radix of N1 if "tag" is 1, or of N2 if "tag" is 2.

+>

+> Output Specification: +>

+>

For each test case, print in one line the radix of the other number so that the equation N1 = N2 is true. If the equation is impossible, print "Impossible". If the solution is not unique, output the smallest possible radix. +>

+> Sample Input 1:
+> 6 110 1 10
+> 
+> Sample Output 1:
+> 2
+> 
+> Sample Input 2:
+> 1 ab 1 2
+> 
+> Sample Output 2:
+> Impossible
+> 
+>
+ +## 思路 + +一开始以为数字是0-9a-z,那进制最多就是36,结果一小半都错。看了一下别人的方法,才知道进制并没有任何限制。 + +几个要点: +- 用二分查找法(binary search)。因为radix范围是2~LLONG_MAX,逐一遍历肯定是算到地老天荒的。(LLONG_MAX是long long能存储的最大值,定义在头文件limits.h中) +- 运算中过大的数的处理。如果预计计算结果会大于long long int范围,就返回一个代表溢出的结果(如-1)。 +- 关于“If the solution is not unique, output the smallest possible radix.”,这只能发生在未知进制的数只有一位的情况下,它在任何进制下的值都是一样的,这样如果它等于已知数,最小可能进制就是这个数加1,否则就无解。 + +当然我在处理溢出的时候并没有像大多数人(我也不知道是不是大多数,因为10篇博客有8篇都是雷同的)将结果和已知数对比,而是和LLONG_MAX对比,当然不能直接将结果算出来了,具体看我的代码base10函数。 +另外由于我是在LLONG_MAX以内查找,因此二分查找第一次求平均时就是LLONG_MAX加一个数除以二,这里就溢出了,所以这里也做了防溢出。具体在binsearch函数。 + +P.S. 其实这道题用long long类型存储在严格的分析下也是不够的,比如说最大的情况(worst scenario):所给的数是zzzzzzzzzz,它的进制是2^31-1^(INT_MAX,既然没限制,那么这就是最小合理上限),另一个数是10,那后者的进制可以是前者的10进制的值,约是:3.5*10^85^,远远大于C语言内置数据类型的容量。我也想过实现下满足这样苛刻要求的方法,用多个int表示一个超大的数,但是运算上比较麻烦,没个200行写不下,所以就暂时按照大多数人的思路写了,反正能AC╭(╯^╰)╮ + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1010.c),欢迎交流 +```c +#include +#include +#include +#include + +#define OVERFLOW -1 +#define NOTFOUNT -1 +#define CBASE10(C) ((C) >= '0' && (C) <= '9' ? (C) - '0' : (C) - 'a' + 10) + +/* Calculate decimal value of a text-form number s under a radix */ +long long base10(char *s, long long radix) +{ + long long n, sum; + for(sum = 0; *s; s++) + { + n = CBASE10(*s); + if((LLONG_MAX - n) / radix < sum) /* overflow */ + return OVERFLOW; + sum = sum * radix + n; + } + return sum; +} + +/* Find the smallest possible radix of a numbers */ +int minradix(char *s) +{ /* Simply the largest digit in the number plus 1 */ + char r; + for(r = '0'; *s; s++) + if(*s > r) + r = *s; + return CBASE10(r) + 1; +} + +/* Use binary search to locate radix of s which makes it equals n */ +long long binsearch(char *s, long long n, long long rmin, long long rmax) +{ + long long r, m; + while(rmax >= rmin) + { + r = rmin + (rmax - rmin) / 2; /* avoid (rmin + rmax) overflow */ + if((m = base10(s, r)) > n || m == OVERFLOW) + rmax = r - 1; + else if(m < n) + rmin = r + 1; + else + return r; + } + return NOTFOUNT; +} + +int main() +{ + int tag, radix; + long long N1, rmin, rmax, r; + char buf1[11], buf2[11], *S1, *S2; + + /* Make S1 point to the number with known radix, S2 to the other */ + scanf("%s %s %d %d", buf1, buf2, &tag, &radix); + if(tag == 1) S1 = buf1, S2 = buf2; + if(tag == 2) S1 = buf2, S2 = buf1; + + N1 = base10(S1, radix); /* Corresponding decimal of S1 */ + rmin = minradix(S2); /* Smallest possible radix of S2 */ + rmax = LLONG_MAX; /* Largest possible radix of S2 */ + if(strlen(S2) == 1) /* If so, N2 will be same value under any radix */ + { /* rmin - 1 (naturally equals N2) equals N1 */ + if(N1 == rmin - 1) printf("%lld", rmin); + else printf("Impossible"); + } + else /* Binary search to find the radix of N2 */ + { + r = binsearch(S2, N1, rmin, rmax); + if(r != NOTFOUNT) printf("%lld", r); + else printf("Impossible"); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1011.md b/_posts/2018-05-03-a1011.md new file mode 100644 index 0000000..f873624 --- /dev/null +++ b/_posts/2018-05-03-a1011.md @@ -0,0 +1,70 @@ +--- +layout: post +title: "1011. World Cup Betting (20)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

With the 2010 FIFA World Cup running, football fans the world over were becoming increasingly excited as the best players from the best teams doing battles for the World Cup trophy in South Africa. Similarly, football betting fans were putting their money where their mouths were, by laying all manner of World Cup bets.

+>

Chinese Football Lottery provided a "Triple Winning" game. The rule of winning was simple: first select any three of the games. Then for each selected game, bet on one of the three possible results -- namely W for win, T for tie, and L for lose. There was an odd assigned to each result. The winner's odd would be the product of the three odds times 65%.

+>

For example, 3 games' odds are given as the following:

+>
+>  W    T    L
+> 1.1  2.5  1.7
+> 1.2  3.0  1.6
+> 4.1  1.2  1.1
+> 
+>

To obtain the maximum profit, one must buy W for the 3rd game, T for the 2nd game, and T for the 1st game. If each bet takes 2 yuans, then the maximum profit would be (4.1*3.0*2.5*65%-1)*2 = 37.98 yuans (accurate up to 2 decimal places).

+>

Input

+>

Each input file contains one test case. Each case contains the betting information of 3 games. Each game occupies a line with three distinct odds corresponding to W, T and L.

+>

Output

+>

For each test case, print in one line the best bet of each game, and the maximum profit accurate up to 2 decimal places. The characters and the number must be separated by one space.

+> Sample Input
+> 1.1 2.5 1.7
+> 1.2 3.0 1.6
+> 4.1 1.2 1.1
+> 
+> Sample Output
+> T T W 37.98
+> 
+>
+ +## 思路 + + +比较简单的题,写了个不用数组的代码。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1011.c),欢迎交流 +```c +#include + +int main() +{ + char c; + float odd, maxodd, maxprofit = 1; + + for(int i = 0; i < 3; i++) + { + maxodd = 0; + for(int j = 0; j < 3; j++) + { + scanf("%f", &odd); + if(maxodd < odd) + { + maxodd = odd; + c = "WTL"[j]; + } + } + printf("%c ", c); + maxprofit *= maxodd; + } + printf("%.2f", (maxprofit * 0.65 - 1) * 2); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1012.md b/_posts/2018-05-03-a1012.md new file mode 100644 index 0000000..1856fb5 --- /dev/null +++ b/_posts/2018-05-03-a1012.md @@ -0,0 +1,126 @@ +--- +layout: post +title: "1012. The Best Rank (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

To evaluate the performance of our first year CS majored students, we consider their grades of three courses only: C - C Programming Language, M - Mathematics (Calculus or Linear Algebra), and E - English. At the mean time, we encourage students by emphasizing on their best ranks -- that is, among the four ranks with respect to the three courses and the average grade, we print the best rank for each student.

+>

For example, The grades of C, M, E and A - Average of 4 students are given as the following:

+>
+> StudentID  C  M  E  A
+> 310101     98 85 88 90
+> 310102     70 95 88 84
+> 310103     82 87 94 88
+> 310104     91 91 91 91
+> 
+>

Then the best ranks for all the students are No.1 since the 1st one has done the best in C Programming Language, while the 2nd one in Mathematics, the 3rd one in English, and the last one in average.

+>

Input

+>

Each input file contains one test case. Each case starts with a line containing 2 numbers N and M (<=2000), which are the total number of students, and the number of students who would check their ranks, respectively. Then N lines follow, each contains a student ID which is a string of 6 digits, followed by the three integer grades (in the range of [0, 100]) of that student in the order of C, M and E. Then there are M lines, each containing a student ID.

+>

Output

+>

For each of the M students, print in one line the best rank for him/her, and the symbol of the corresponding rank, separated by a space.

+>

The priorities of the ranking methods are ordered as A > C > M > E. Hence if there are two or more ways for a student to obtain the same best rank, output the one with the highest priority.

+>

If a student is not on the grading list, simply output "N/A".

+> Sample Input
+> 5 6
+> 310101 98 85 88
+> 310102 70 95 88
+> 310103 82 87 94
+> 310104 91 91 91
+> 310105 85 90 90
+> 310101
+> 310102
+> 310103
+> 310104
+> 310105
+> 999999
+> 
+> Sample Output
+> 1 C
+> 1 M
+> 1 E
+> 1 A
+> 3 A
+> N/A
+> 
+>
+ +## 思路 + +**计算排名的方法:** + +很多人用的结构体排序,这样是比较耗时的(尤其是基数很大的时候)。 + +我的方法是在读取学生成绩的时候顺便记录0~100分各有多少人,然后根据得分人数计算排名。举个例子: + +- 如果100分有1人,99分有2人,98分有3人,97分有4人。 + +那么 + +- 得100分的排名是1,得99分的排名是2(并列),98分的排名是4(并列),97分的排名是7(并列)。 + +规律是什么?如果学生获得的分数分别有a1>a2>a3>...>ai>...,那么 + +- 分数a[i]的名次 = 分数a[i-1]的名次 + 分数a[i-1]的人数 +- 分数a[1]的名次 = 1 + +这其实是递归定义,如果定义一个不存在的高分a0(比如101分)人数=1(代码中一开始的初始化就是做这个),那么上面的关系就简化成了 + +- **分数a[i]的名次 = a[0]~a[i-1]的人数总和**,(i>0) + +可以验证一下:97分的排名=98~101分人数总和=3+2+1+1=7 + +**平均分计算方法:** + +仔细看题目中给的例子,平均分是四舍五入的,不过我试过在算平均分的时候直接3个数相加除以3,还是能AC。所以要不是题目没有设置这样的测试点,要不就是题目认为两种算法结果都正确。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1012.c),欢迎交流 +```c +#include + +typedef struct Student{ int ID, score[4]; } Student; + +int main() +{ + /* scores array stores the number of students of any score */ + int N, M, ID, scores[4][102] = {{0}}; + + /* Setting a score of 101 is to make it easy to calculate rank */ + scores[0][101] = scores[1][101] = scores[2][101] = scores[3][101] = 1; + Student students[2000]; + + scanf("%d %d", &N, &M); + for(int i = 0; i < N; i++) + { /* record all the scores for every student: A, C, M, E */ + Student *s = students + i; + scanf("%d %d %d %d", &s->ID, &s->score[1], &s->score[2], &s->score[3]); + s->score[0] = (s->score[1] + s->score[2] + s->score[3] + 1) / 3; + for(int j = 0; j < 4; j++) /* +1 for rounding */ + scores[j][s->score[j]]++; /* record how many got this score */ + } + + for(int i = 0; i < M; i++) + { + int max = 3, ranks[4] = {0}, stu; + + scanf("%d", &ID); + for(stu = 0; stu < N && students[stu].ID != ID; stu++) ; /* find */ + if(stu == N) { puts("N/A"); continue;} + + for(int j = 3; j >= 0; j--) + { /* calculate the rank: sum the counts from score + 1 to 101 */ + /* e.g. score 100 will have rank 1, since scores[101] is 1 */ + for(int score = 100; score >= students[stu].score[j]; score--) + ranks[j] += scores[j][score + 1]; + if(ranks[j] <= ranks[max]) /* The best rank with highest priority */ + max = j; + } + printf("%d %c\n", ranks[max], "ACME"[max]); + } +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1013.md b/_posts/2018-05-03-a1013.md new file mode 100644 index 0000000..94451c5 --- /dev/null +++ b/_posts/2018-05-03-a1013.md @@ -0,0 +1,178 @@ +--- +layout: post +title: "1013. Battle Over Cities (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.

+>

For example, if we have 3 cities and 2 highways connecting city1-city2 and city1-city3. Then if city1 is occupied by the enemy, we must have 1 highway repaired, that is the highway city2-city3.

+>

Input

+>

Each input file contains one test case. Each case starts with a line containing 3 numbers N (&lt1000;), M and K, which are the total number of cities, the number of remaining highways, and the number of cities to be checked, respectively. Then M lines follow, each describes a highway by 2 integers, which are the numbers of the cities the highway connects. The cities are numbered from 1 to N. Finally there is a line containing K numbers, which represent the cities we concern.

+>

Output

+>

For each of the K cities, output in a line the number of highways need to be repaired if that city is lost.

+> Sample Input
+> 3 2 3
+> 1 2
+> 1 3
+> 1 2 3
+> 
+> Sample Output
+> 1
+> 0
+> 0
+> 
+>
+ +## 思路 + +题目即要求我们计算这些城市有几个内部相连的部分。 + +思路是对这些城市和公路组成的图进行多次遍历,直至所有城市都被遍历位置,遍历次数减1即为需要修复的公路数量。 + +具体遍历时,逐一城市进行一个更高层的循环,跳过已遍历过的即可。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1013.c),欢迎交流 +```c +#include + +#define MAX 1000 + +void DFS(int v, int known[], int G[][MAX]) +{ + known[v] = 1; + for(int i = 0; i < MAX; i++) + if((G[v][i] || G[i][v]) && !known[i]) + DFS(i, known, G); +} + +int main() +{ + int N, M, K; + scanf("%d %d %d", &N, &M, &K); + + int Graph[MAX][MAX] = {{0}}; + + int city1, city2; + for(int i = 0; i < M; i++) + { + scanf("%d %d", &city1, &city2); + Graph[city1][city2] = 1; + } + + for(int i = 0; i < K; i++) + { + int known[MAX] = {0}, lostcity, count = 0; + scanf("%d", &lostcity); + + known[lostcity] = 1; + for(int i = 1; i <= N; i++) if(!known[i]) + { + DFS(i, known, Graph); + count++; + } + printf("%d\n", count - 1); + } +} + + +///* previous linked list + BFS version, passed */ +//#include +//#include + +//typedef struct Edge{ + //int id; + //struct Edge *iter; +//} *Edge; + +//typedef struct Vertex{ + //int known; + //Edge adj; +//} *Vertex; + +//typedef struct Graph{ + //int nvertex; + //int nedge; + //Vertex vs; + //Edge es; +//} *Graph; + +//Graph ReadGraph(int nvertex, int nedge) +//{ + ///* create new graph */ + //Graph G = (Graph)malloc(sizeof(struct Graph)); + //G->nvertex = nvertex; + //G->nedge = nedge; + //G->vs = (Vertex)malloc(nvertex * sizeof(struct Vertex)); + //G->es = (Edge)malloc(nedge * sizeof(struct Edge)); + //for(int i = 0; i < G->nvertex; i++) + //G->vs[i].adj = NULL; + ///* read */ + //int start, end; + //for(int i = 0; i < G->nedge; i++) + //{ + //scanf("%d %d", &start, &end); + //start--, end--; + //if(start > end) {int temp = start; start = end; end = temp;} + //G->es[i].id = end; + //G->es[i].iter = G->vs[start].adj; + //G->vs[start].adj = &G->es[i]; + //} + //return G; +//} + +//typedef struct Queue{ + //int front; + //int rear; + //Vertex list[1000]; +//} *Queue; + +//void Traversal(Graph G, Vertex start) +//{ + //Queue Q = (Queue)malloc(sizeof(struct Queue)); + //Q->rear = Q->front = 0; + //Q->list[++Q->front] = start; + //start->known = 1; + + //while(Q->front != Q->rear) + //{ + //Q->rear = Q->rear == 999 ? 0 : Q->rear + 1; + //Vertex v = Q->list[Q->rear]; + //for(Edge e = v->adj; e; e = e->iter) if(!G->vs[e->id].known) + //{ +//// printf("en: %d ", e->id); + //Q->front = Q->front == 999 ? 0 : Q->front + 1; + //Q->list[Q->front] = &G->vs[e->id]; + //G->vs[e->id].known = 1; + //} + //} +//} + +//int main() +//{ + //int N, M, K, lostcity, count; + //scanf("%d %d %d", &N, &M, &K); + //Graph G = ReadGraph(N, M); + //for(int i = 0; i < K; i++) + //{ + //scanf("%d", &lostcity); + ///* Reset the graph */ + //lostcity--, count = 0; + //for(int i = 0; i < G->nvertex; i++) + //G->vs[i].known = 0; + ///* Traversal */ + //G->vs[lostcity].known = 1; + //for(int i = 0; i < G->nvertex; i++) if(!G->vs[i].known) + //{ + //Traversal(G, &G->vs[i]); + //count++; + //} + //printf("%d\n", count - 1); + //} +//} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1014.md b/_posts/2018-05-03-a1014.md new file mode 100644 index 0000000..6f06f8c --- /dev/null +++ b/_posts/2018-05-03-a1014.md @@ -0,0 +1,191 @@ +--- +layout: post +title: "1014. Waiting in Line (30)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

Suppose a bank has N windows open for service. There is a yellow line in front of the windows which devides the waiting area into two parts. The rules for the customers to wait in line are:

+>
    +>
  • The space inside the yellow line in front of each window is enough to contain a line with M customers. Hence when all the N lines are full, all the customers after (and including) the (NM+1)st one will have to wait in a line behind the yellow line. +>
  • Each customer will choose the shortest line to wait in when crossing the yellow line. If there are two or more lines with the same length, the customer will always choose the window with the smallest number. +>
  • Customer[i] will take T[i] minutes to have his/her transaction processed. +>
  • The first N customers are assumed to be served at 8:00am. +>
+>

Now given the processing time of each customer, you are supposed to tell the exact time at which a customer has his/her business done.

+>

For example, suppose that a bank has 2 windows and each window may have 2 customers waiting inside the yellow line. There are 5 customers waiting with transactions taking 1, 2, 6, 4 and 3 minutes, respectively. At 08:00 in the morning, customer1 is served at window1 while customer2 is served at window2. Customer3 will wait in front of window1 and customer4 will wait in front of window2. Customer5 will wait behind the yellow line.

+>

At 08:01, customer1 is done and customer5 enters the line in front of window1 since that line seems shorter now. Customer2 will leave at 08:02, customer4 at 08:06, customer3 at 08:07, and finally customer5 at 08:10.

+>

Input

+>

Each input file contains one test case. Each case starts with a line containing 4 positive integers: N (<=20, number of windows), M (<=10, the maximum capacity of each line inside the yellow line), K (<=1000, number of customers), and Q (<=1000, number of customer queries).

+>

The next line contains K positive integers, which are the processing time of the K customers.

+>

The last line contains Q positive integers, which represent the customers who are asking about the time they can have their transactions done. The customers are numbered from 1 to K.

+>

Output

+>

For each of the Q customers, print in one line the time at which his/her transaction is finished, in the format HH:MM where HH is in [08, 17] and MM is in [00, 59]. Note that since the bank is closed everyday after 17:00, for those customers who cannot be served before 17:00, you must output "Sorry" instead.

+> Sample Input
+> 2 2 7 5
+> 1 2 6 4 3 534 2
+> 3 4 5 6 7
+> 
+> Sample Output
+> 08:07
+> 08:06
+> 08:10
+> 17:00
+> Sorry
+> 
+>
+ +## 思路 + + +### 题意解读: + +银行有N个窗口, 每个窗口允许排M个顾客. 一大早8点所有顾客商量好蜂拥而入, +那么情况有两种: 1, 顾客全部能排在队伍里; 2, 顾客人数太多, 有人排在外面, +要等前面的顾客完成离开, 再进入列列. + +**注意点**: + +- 时间截止的标准是: 进入列列不晚于下午5点即可. 也就是说以进入列列时间为标准, + 中间用多长时间都没问题. 这个点包括我很多人肯定第一感觉是理解错的. + +### 代码结构 + +解读题目时提到可以存在两种情况, 我将这两种情况合并了一下: + +- 顾客一共有K个, 如果`K<=M*N`, 那么我用下面这图表示人次的流动情况(E表示入列, D表示 +出列, -表示此时无此操作): + + ``` + 时间顺序或者操作顺序--> + 出列: -----------------DDD...(K*D)...DDD + 入列: EEE...(K*E)...EEE----------------- + ``` + + 即表示一起进行K次入列, 然后一起进行K次出列, 就完成了. + +- 如果`K>M*N`, 那么其中有一段时间会交替有人出列/入列, 表示起来就是: + + ``` + 时间顺序或者操作顺序--> + 出列: ----(M*N)-----DDDDDD...(K*D)...DDD + 入列: EEE...(K*E)...EEEEEE----(M*N)----- + ``` + + 这表示前M*N次只有入列, 中间有顾客出列, 有顾客入列, 最后M*N次只有出列. + +总结这两种情况, 都是前K次是入列, 后K次是出列, 只需再计算出总的操作次数(出入记为 +一次)即可, 其实就是`max(2*K, M*N+K)`. + +这样代码结构就是 + +``` +循环max(2*K, M*N+K)次: + 如果是后K次: + 出列 + 如果是前K次: + 入列 +``` + +### 计时方法 + +数据结构: + +- 用一个数组(长度至少为K+1, 后面解释)先记录每个顾客的所需时间. 后面操作将这个时间 + 更新为顾客入列时间. + + 时间为距离早8点的分钟数. + + 如实按顾客编号(即从1开始)记录, 角标为0的元素保持为0. + +- 用N个队列模拟相关过程, 记录内容为顾客标号, 但是队列长度至少为M+1(后面解释) + +操作主要在入列时(因为入列时间很重要) + +入列时: + +- 找到长度最短的队列 +- 找到前面一个人的(包括标号0)对应时间 +- 将将要入列的人的时间加上上述时间, 即得到了此人的入列时间 + +出列时: + +- 只需找到所有队列队首中时间最早的对其出列即可 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1014.c),欢迎交流 +```c +#include + +#define LATE_FLAG -1 +#define FORWARD(I) ((I) = ((I) == 10) ? 0 : ((I) + 1)) +#define TIME_FRONT(I) time[queue[I][front[I]]] +#define TIME_REAR_PREVIOUS(I) time[queue[I][rear[I] == 0 ? 10 : (rear[I] - 1)]] + +int main() +{ + int N, M, K, Q, query; + int time[1000], queue[20][11] = {{0}}; + int front[20] = {0}, rear[20] = {0}, length[20] = {0}; + + scanf("%d %d %d %d", &N, &M, &K, &Q); + for(int i = 1; i <= K; i++) + scanf("%d", time + i); + + /* Total number of operations */ + int count = (K < M * N) ? (2 * K) : (K + M * N); + /* Doing dequeues and enqueues for every customer */ + for(int i = 1; i <= count; i++) + { + if(i > count - K) /* Dequeue in the last K operations */ + { + /* Find the next customer */ + int time_span = 9999, next = -1; + for(int j = 0; j < N; j++) if(length[j]) + { + if(TIME_FRONT(j) < time_span) + next = j, time_span = TIME_FRONT(j); + else if(next == -1 && TIME_FRONT(j) == LATE_FLAG) + next = j; + } + /* Dequeue */ + FORWARD(front[next]); + length[next]--; + } + if(i <= K) /* Enqueue in the first K operations */ + { + /* Find shortest queue */ + int shortest = 0; + for(int j = 0; j < N; j++) + if(length[shortest] > length[j]) + shortest = j; + /* Set flag or add time */ + int previous_time = TIME_REAR_PREVIOUS(shortest); + if(previous_time >= 9 * 60 || previous_time == LATE_FLAG) + time[i] = LATE_FLAG; + else + time[i] += previous_time; + /* Enqueue */ + queue[shortest][rear[shortest]] = i; + FORWARD(rear[shortest]); + length[shortest]++; + } + } + + /* Read queries and print answers */ + for(int i = 0; i < Q; i++) + { + scanf("%d", &query); + if(time[query] != LATE_FLAG) + printf("%02d:%02d\n", 8 + time[query] / 60, time[query] % 60); + else + printf("Sorry\n"); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1015.md b/_posts/2018-05-03-a1015.md new file mode 100644 index 0000000..6eb8f8a --- /dev/null +++ b/_posts/2018-05-03-a1015.md @@ -0,0 +1,79 @@ +--- +layout: post +title: "1015. Reversible Primes (20)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

A reversible prime in any number system is a prime whose "reverse" in that number system is also a prime. For example in the decimal system 73 is a reversible prime because its reverse 37 is also a prime. +>

+>

Now given any two positive integers N (< 105) and D (1 < D <= 10), you are supposed to tell if N is a reversible prime with radix D. +> +>

+> Input Specification: +>

+>

The input file consists of several test cases. Each case occupies a line which contains two integers N and D. The input is finished by a negative N.

+>

+> Output Specification: +>

+>

For each test case, print in one line "Yes" if N is a reversible prime with radix D, or "No" if not.

+> Sample Input:
+> 73 10
+> 23 2
+> 23 10
+> -2
+> 
+> Sample Output:
+> Yes
+> Yes
+> No
+> 
+>

+ +## 思路 + + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1015.c),欢迎交流 +```c +#include + +int iPrime(int N) +{ + if(N == 0 || N == 1) + return 0; + for(int i = 2; i * i <= N; i++) + if(N % i == 0) + return 0; + return 1; +} + +int Rev(int N, int D) +{ + int Nrev; + for(Nrev = 0; N; N /= D) + { + Nrev *= D; + Nrev += N % D; + } + return Nrev; +} + +int main() +{ + int N, D; + scanf("%d", &N); + while(N >= 0) + { + scanf("%d", &D); + puts(iPrime(N) && iPrime(Rev(N, D)) ? "Yes" : "No"); + scanf("%d", &N); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1016.md b/_posts/2018-05-03-a1016.md new file mode 100644 index 0000000..6bfae26 --- /dev/null +++ b/_posts/2018-05-03-a1016.md @@ -0,0 +1,145 @@ +--- +layout: post +title: "1016. Phone Bills (25)" +categories: PAT-A +tags: [PAT-A] +--- +## 题目 + +>
+>

A long-distance telephone company charges its customers by the following rules:

+>

+> Making a long-distance call costs a certain amount per minute, depending on the time of day when the call is made. When a customer starts connecting a long-distance call, the time will be recorded, and so will be the time when the customer hangs up the phone. Every calendar month, a bill is sent to the customer for each minute called (at a rate determined by the time of day). Your job is to prepare the bills for each month, given a set of phone call records.

+>

+> Input Specification: +>

+>

Each input file contains one test case. Each case has two parts: the rate structure, and the phone call records.

+>

The rate structure consists of a line with 24 non-negative integers denoting the toll (cents/minute) from 00:00 - 01:00, the toll from 01:00 - 02:00, and so on for each hour in the day.

+>

The next line contains a positive number N (<= 1000), followed by N lines of records. Each phone call record consists of the name of the customer (string of up to 20 characters without space), the time and date (mm:dd:hh:mm), and the word "on-line" or "off-line".

+>

For each test case, all dates will be within a single month. Each "on-line" record is paired with the chronologically next record for the same customer provided it is an "off-line" record. Any "on-line" records that are not paired with an "off-line" record are ignored, as are "off-line" records not paired with an "on-line" record. It is guaranteed that at least one call is well paired in the input. You may assume that no two records for the same customer have the same time. Times are recorded using a 24-hour clock.

+>

+> Output Specification: +>

+>

For each test case, you must print a phone bill for each customer.

+>

Bills must be printed in alphabetical order of customers' names. +> For each customer, first print in a line the name of the customer and the month of the bill in the format shown by the sample. Then for each time period of a call, print in one line the beginning and ending time and date (dd:hh:mm), the lasting time (in minute) and the charge of the call. The calls must be listed in chronological order. Finally, print the total charge for the month in the format shown by the sample.

+> Sample Input:
+> 10 10 10 10 10 10 20 20 20 15 15 15 15 15 15 15 20 30 20 15 15 10 10 10
+> 10
+> CYLL 01:01:06:01 on-line
+> CYLL 01:28:16:05 off-line
+> CYJJ 01:01:07:00 off-line
+> CYLL 01:01:08:03 off-line
+> CYJJ 01:01:05:59 on-line
+> aaa 01:01:01:03 on-line
+> aaa 01:02:00:01 on-line
+> CYLL 01:28:15:41 on-line
+> aaa 01:05:02:24 on-line
+> aaa 01:04:23:59 off-line
+> 
+> Sample Output:
+> CYJJ 01
+> 01:05:59 01:07:00 61 $12.10
+> Total amount: $12.10
+> CYLL 01
+> 01:06:01 01:08:03 122 $24.40
+> 28:15:41 28:16:05 24 $3.85
+> Total amount: $28.25
+> aaa 01
+> 02:00:01 04:23:59 4318 $638.80
+> Total amount: $638.80
+> 
+>
+ +## 思路 + + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1016.c),欢迎交流 +```c +#include +#include +#include + +/* Structure to store phone records */ +typedef struct { + char name[21]; + int month, day, hour, min, time, state; +}Record, *pRecord; + +/* Compare first by name, then by date and time */ +int cmp(const void *record1, const void *record2) +{ + pRecord r1 = *(pRecord*)record1, r2 = *(pRecord*)record2; + return strcmp(r1->name, r2->name) ? + strcmp(r1->name, r2->name) : r1->time - r2->time; +} + +/* Calculate the charge of the call with start record p1 and end record p2 */ +int calccharge(pRecord p1, pRecord p2, int toll[]) +{ + int charge = 0, start = p1->time, end = p2->time, h, time1, time2; + + for(time1 = start; time1 < end; time1 = time2) + { /* Add the charge hour by hour */ + time2 = (time1 / 60 + 1) * 60; /* time2 will be the time of next hour */ + h = time1 / 60 % 24; /* h will be the index of the hour */ + charge += ((time2 > end ? end : time2) - time1) * toll[h]; + } + + return charge; +} + +int main() +{ + char state[9]; + int N, toll[24], charge, charge_total = 0; + Record records[1001] = {0}; + pRecord precords[1001] = {0}, *p = precords; + + /* Read data */ + for(int i = 0; i < 24; i++) + scanf("%d", toll + i); + scanf("%d", &N); + for(int i = 0; i < N; i++, p++) + { + *p = records + i; + scanf("%s %d:%d:%d:%d %s", (*p)->name, + &(*p)->month, &(*p)->day, &(*p)->hour, &(*p)->min, state); + (*p)->time = ((*p)->day * 24 + (*p)->hour) * 60 + (*p)->min; + (*p)->state = strcmp(state, "on-line") ? 0 : 1; + } + + /* Sort first by name, then by date and time */ + qsort(precords, N, sizeof(pRecord), cmp); + + /* Print phone bill one by one */ + for(p = precords + 1; *p; p++) + { + if(strcmp((*p)->name, (*(p - 1))->name)) + { /* A new customer, print last total amount if any */ + if(charge_total) + printf("Total amount: $%.2f\n", charge_total * 1e-2); + charge_total = 0; + } + else if((*(p - 1))->state == 1 && (*p)->state == 0) + { /* Still the same customer, finding on/off record pair */ + if(charge_total == 0) + printf("%s %02d\n", (*p)->name, (*p)->month); + charge = calccharge(*(p - 1), *p, toll); + charge_total += charge; + /* Print info of this call */ + printf("%02d:%02d:%02d %02d:%02d:%02d %d $%.2f\n", + (*(p - 1))->day, (*(p - 1))->hour, (*(p - 1))->min, + (*p)->day, (*p)->hour, (*p)->min, + (*p)->time - (*(p - 1))->time, charge * 1e-2); + } + } + if(charge_total) + printf("Total amount: $%.2f\n", charge_total * 1e-2); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-a1017.md b/_posts/2018-05-03-a1017.md new file mode 100644 index 0000000..42aa5bc --- /dev/null +++ b/_posts/2018-05-03-a1017.md @@ -0,0 +1,136 @@ +--- +layout: post +title: "1017. Queueing at Bank (25)" +categories: PAT-A +tags: [PAT-A] +--- + +## 题目 + +>
+>

Suppose a bank has K windows open for service. There is a yellow line in front of the windows which devides the waiting area into two parts. All the customers have to wait in line behind the yellow line, until it is his/her turn to be served and there is a window available. It is assumed that no window can be occupied by a single customer for more than 1 hour.

+>

Now given the arriving time T and the processing time P of each customer, you are supposed to tell the average waiting time of all the customers.

+>

+> Input Specification: +>

+>

Each input file contains one test case. For each case, the first line contains 2 numbers: N (<=10000) - the total number of customers, and K (<=100) - the number of windows. Then N lines follow, each contains 2 times: HH:MM:SS - the arriving time, and P - the processing time in minutes of a customer. Here HH is in the range [00, 23], MM and SS are both in [00, 59]. It is assumed that no two customers arrives at the same time.

+>

Notice that the bank opens from 08:00 to 17:00. Anyone arrives early will have to wait in line till 08:00, and anyone comes too late (at or after 17:00:01) will not be served nor counted into the average.

+>

+> Output Specification: +>

+>

For each test case, print in one line the average waiting time of all the customers, in minutes and accurate up to 1 decimal place.

+> Sample Input:
+> 7 3
+> 07:55:00 16
+> 17:00:01 2
+> 07:59:59 15
+> 08:01:00 60
+> 08:00:00 30
+> 08:00:02 2
+> 08:03:00 10
+> 
+> Sample Output:
+> 8.2
+> 
+>
+ +## 思路 + +这道题虽然和1014很像, 但是实际上要简单得多. 因为1014题需要具体模拟队列的出入操作, +但是这道题则不需要队列这样的数据结构. + +需要注意的点: + +- 晚于下午五点的顾客才会不予处理, 之前到来的不管等到多晚, 都会处理 +- 处理时长长于一小时需降为一小时 + +数据结构: + +- `struct[N]`: 记录顾客时间信息, 结构体包含顾客到达时间和处理时长两个整形 +- `int[K]`: 记录每个窗口的顾客完成时间信息 + +大体思路: + +- 读取顾客时间信息, 按照到达时间排序, 以便处理 +- 遍历所有到达时间不晚于17:00的顾客: + - 找到完成时间最早的窗口 + - 如果窗口完成时间晚于当前顾客到达时间 + - 两者之差累加到等待时间 + - 窗口时间累加上顾客处理时长 + - 否则无需等待, 窗口时间更新为顾客到达时间加顾客处理时长 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATAdvanced/1017.c),欢迎交流 +```c +#include +#include + +typedef struct Customer{ + int start, len; +}Customer; + +/* Compare two Customer stucture by start time */ +int cmp(const void *a, const void *b) +{ + Customer c1 = *(Customer*)a; + Customer c2 = *(Customer*)b; + return c1.start - c2.start; +} + +int main() +{ + int N, K, earliest, i; + int HH, MM, SS; + int wait_time = 0, queue_time[100] = {0}; + Customer customers[10000], *p; + + scanf("%d %d", &N, &K); + for(int i = 0; i < N; i++) + { + scanf("%d:%d:%d %d", &HH, &MM, &SS, &customers[i].len); + /* Relative time to 08:00:00 */ + customers[i].start = SS + 60 * (MM + 60 * (HH - 8)); + customers[i].len *= 60; + } + + qsort(customers, N, sizeof(Customer), cmp); + + for(i = 0; i < N; i++) + { + p = customers + i; + + /* Find the queue number which will finish next */ + earliest = 0; + for(int i = 0; i < K; i++) + if(queue_time[i] < queue_time[earliest]) + earliest = i; + + /* later than 17:00:00 */ + if(p->start > (17 - 8) * 3600) + break; + /* processing time longer than one hour */ + if(p->len > 3600) + p->len = 3600; + + /* increase total waiting time and modify the time of each queue */ + if(p->start < queue_time[earliest]) + { + wait_time += queue_time[earliest] - p->start; + queue_time[earliest] += p->len; + } + else + { + queue_time[earliest] = p->start + p->len; + } + } + + if(i) + printf("%.1f", wait_time / 60.0 / i); + else + printf("0.0"); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1001.md b/_posts/2018-05-03-b1001.md new file mode 100644 index 0000000..97e360c --- /dev/null +++ b/_posts/2018-05-03-b1001.md @@ -0,0 +1,52 @@ +--- +layout: post +title: "1001. 害死人不偿命的(3n+1)猜想 (15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

卡拉兹(Callatz)猜想:

+>

对任何一个自然数n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把(3n+1)砍掉一半。这样一直反复砍下去,最后一定在某一步得到n=1。卡拉兹在1950年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学生们无心学业,一心只证(3n+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展…… +>

+>

我们今天的题目不是证明卡拉兹猜想,而是对给定的任一不超过1000的正整数n,简单地数一下,需要多少步(砍几下)才能得到n=1? +> +>

输入格式:每个测试输入包含1个测试用例,即给出自然数n的值。

+>

输出格式:输出从n计算到1需要的步数。

+> 输入样例:
+> 3
+> 
+> 输出样例:
+> 5
+> 
+>

+ +## 思路 + +很简单的一道题,读取数字后按照规则计算,记录计算次数即可。 + + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1001.c),欢迎交流 +```c +#include + +int main() +{ + int number, steps; + scanf("%d", &number); + + for(steps = 0; number != 1; steps++) + if(number % 2 == 0) + number /= 2; + else + number = (3 * number + 1) / 2; + + printf("%d", steps); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1002.md b/_posts/2018-05-03-b1002.md new file mode 100644 index 0000000..e7fe0a6 --- /dev/null +++ b/_posts/2018-05-03-b1002.md @@ -0,0 +1,57 @@ +--- +layout: post +title: "1002. 写出这个数 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字。 +> +>

+>

输入格式:每个测试输入包含1个测试用例,即给出自然数n的值。这里保证n小于10100。

+>

输出格式:在一行内输出n的各位数字之和的每一位,拼音数字间有1 +> +> 空格,但一行中最后一个拼音数字后没有空格。 +>

+> 输入样例:
+> 1234567890987654321123456789
+> 
+> 输出样例:
+> yi san wu
+> 
+>
+ +## 思路 + + +也是很简单的一道题。 +1. 先读取自然数n,边读取边计算各位数字的和,只需一个字符和一个整型。 +2. 构建一个0-9拼音的字符串数组,不低于100的输出百位,不低于10的输出十位,最后输出个位即可。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1002.c),欢迎交流 +```c +#include + +int main() +{ + int sum = 0; + char c, *pinyins[] = {"ling", "yi", "er", "san", "si", + "wu", "liu", "qi", "ba", "jiu"}; + + while((c = getchar()) != '\n') + sum += c - '0'; + + if(sum / 100) /* hundreds */ + printf("%s ", pinyins[sum / 100]); + if(sum / 10) /* tens */ + printf("%s ", pinyins[sum / 10 % 10]); + printf("%s", pinyins[sum % 10]); /* units */ + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1003.md b/_posts/2018-05-03-b1003.md new file mode 100644 index 0000000..18afab4 --- /dev/null +++ b/_posts/2018-05-03-b1003.md @@ -0,0 +1,86 @@ +--- +layout: post +title: "1003. 我要通过!(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

“答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于PAT的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。

+>

得到“答案正确”的条件是:

+>

+> 1. 字符串中必须仅有P, A, T这三种字符,不可以包含其它字符;
+> 2. 任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串;
+> 3. 如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 a, b, c 均或者是空字符串,或者是仅由字母 A 组成的字符串。

+> +> 现在就请你为PAT写一个自动裁判程序,判定哪些字符串是可以获得“答案正确”的。
+ +## 思路 + +先理解一下答案正确的条件: + +1. 字符串只能包含`'P', 'A', 'T'`三种字符,简单,有其它字符就是错误的了。 +2. xPATx格式的是正确的。这个条件需要正确理解,当时就是在这条上想错了,结果好多次没有通过。**两个x是代指相同的字符串**。(有些人包括我理解错的原因大概是按照正则表达式的`*`去理解这里的x了) +3. aPbTc正确,aPbATca也正确。翻译一下,就是P和T中间每增加一个A,需要将P之前的内容复制到字符串末尾,得到的新字符串就也是正确的。 + +结合这三点,总结起来就是: +- **只存在`'P', 'A', 'T'`三种字符;** +- **`'P', 'T'`只能出现一次并且按照该顺序先后出现;** +- **P&T之间不能没有A;** +- **T之后A的数量 = P之前A的数量 × P&T中间A的数量。** + +代码实现: + +* 使用三个数`count[3]`记录P之前,P&T之间,T之后A的数量。 +* 为了检测`'P', 'T'`的出现及次序,使用一个标记变量`pos`: + + * 其值在出现P之前为0(使用count[0]记录P之前的A) + * 只有在出现P且其值为0时,将值变为1(使用count[1]记录P&T之间的A) + * 只有在出现T且其值为1时,将其变为2(使用count[2]记录T之后的A) + + 这样即可保证除此之外出现非`'A'`字符的情况都是不符合要求的。pos顺便还能作为count[]的索引。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1003.c),欢迎交流 +```c +#include + +int main() +{ + char c; + int num; + scanf("%d", &num); + while(getchar() != '\n'); /* read the rest of the line, + make sure to start from a new line later */ + for(int i = 0; i < num; i++) + { + /* pos: indicate the position of current index for count[] */ + /* count[3]: number of As; before P, between P & T and after T */ + int pos = 0, count[3] = {0, 0, 0}; + while((c = getchar()) != '\n') + { + if(c == 'A') count[pos]++; /* count 'A's */ + else if(c == 'P' && pos == 0) pos = 1; /* one P before T */ + else if(c == 'T' && pos == 1) pos = 2; /* one T after P */ + else break; /* 'wrong' string */ + } + + if(c == '\n' /* 1. no other characters at end */ + && pos == 2 /* 2. appearance of 'P' and 'T' */ + && count[1] /* 3. existance of 'A' */ + && count[2] == count[1] * count[0]) /* 4. relation between numbers */ + puts("YES"); + else + puts("NO"); + + /* read the rest of the line */ + if(c != '\n') + while(getchar() != '\n'); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1004.md b/_posts/2018-05-03-b1004.md new file mode 100644 index 0000000..ed9b6c9 --- /dev/null +++ b/_posts/2018-05-03-b1004.md @@ -0,0 +1,80 @@ +--- +layout: post +title: "1004. 成绩排名 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

读入n名学生的姓名、学号、成绩,分别输出成绩最高和成绩最低学生的姓名和学号。

+>

输入格式:每个测试输入包含1个测试用例,格式为

+>   第1行:正整数n
+>   第2行:第1个学生的姓名 学号 成绩
+>   第3行:第2个学生的姓名 学号 成绩
+>   ... ... ...
+>   第n+1行:第n个学生的姓名 学号 成绩
+> 
+> 其中姓名和学号均为不超过10个字符的字符串,成绩为0到100之间的一个整数,这里保证在一组测试用例中没有两个学生的成绩是相同的。

+>

输出格式:对每个测试用例输出2行,第1行是成绩最高学生的姓名和学号,第2行是成绩最低学生的姓名和学号,字符串间有1空格。

+> 输入样例:
+> 3
+> Joe Math990112 89
+> Mike CS991301 100
+> Mary EE990830 95
+> 
+> 输出样例:
+> Mike CS991301
+> Joe Math990112
+> 
+>
+ +## 思路 + +只需要3组变量即可:最高、最低和当前。边读取边更新。 + +代码实现: + +成绩的最高、最低初始化为-1和101,这样方便地处理了特殊情况。 + + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1004.c),欢迎交流 +```c +#include +#include + +int main() +{ + int N; + scanf("%d", &N); + + char maxname[11], minname[11], curname[11], + maxid[11], minid[11], curid[11]; + int maxgrade = -1, mingrade = 101, curgrade; + + for(int i = 0; i < N; i++) + { + scanf("%s %s %d", curname, curid, &curgrade); + + if(curgrade > maxgrade) + { + strcpy(maxname, curname); + strcpy(maxid, curid); + maxgrade = curgrade; + } + if(curgrade < mingrade) + { + strcpy(minname, curname); + strcpy(minid, curid); + mingrade = curgrade; + } + } + + printf("%s %s\n%s %s", maxname, maxid, minname, minid); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1005.md b/_posts/2018-05-03-b1005.md new file mode 100644 index 0000000..da29c1e --- /dev/null +++ b/_posts/2018-05-03-b1005.md @@ -0,0 +1,83 @@ +--- +layout: post +title: "1005. 继续(3n+1)猜想 (25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。

+>

当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对n=3进行验证的时候,我们需要计算3、5、8、4、2、1,则当我们对n=5、8、4、2进行验证的时候,就可以直接判定卡拉兹猜想的真伪,而不需要重复计算,因为这4个数已经在验证3的时候遇到过了,我们称5、8、4、2是被3“覆盖”的数。我们称一个数列中的某个数n为“关键数”,如果n不能被数列中的其他数字所覆盖。

+>

现在给定一系列待验证的数字,我们只需要验证其中的几个关键数,就可以不必再重复验证余下的数字。你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。

+>

输入格式:每个测试输入包含1个测试用例,第1行给出一个正整数K(&lt100;),第2行给出K个互不相同的待验证的正整数n(1&ltn;<=100)的值,数字间用空格隔开。

+>

输出格式:每个测试用例的输出占一行,按从大到小的顺序输出关键数字。数字间用1个空格隔开,但一行中最后一个数字后没有空格。

+> 输入样例:
+> 6
+> 3 5 6 7 8 11
+> 
+> 输出样例:
+> 7 6
+> 
+>
+ +## 思路 + +**最简单的思路:** + +记录初始给出的关键数字,对每一个数字进行验证计算,每遇到初始给出的数字时,对其进行标记。标记过的数字便不再进行验证。 + +**思路优化:** + +如果(也是一般)进行从小到大的遍历,验证一个较大数字时,某一步计算得到一个较小数字(并且在初始给出的数字中),即可断定无须继续验证,因为接下来的验证过程和这个较小数字的验证过程是重复的。 + +**代码实现:** + +所给数字范围是1-100,可以使用长度为100(或101,方便数字和索引对应)的数组记录需验证的数字,数字即为索引。这样无须查找排序等操作。初始给出的数字相应记为1,其他置0。 + +验证过程中会遇到大于100的数,注意条件判断,避免数组越界。 + + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1005.c),欢迎交流 +```c +#include + +int main() +{ + int K, n, tabel[101] = {0}; + + scanf("%d", &K); + for(int i = 0; i < K; i++) + { + scanf("%d", &n); + tabel[n] = 1; + } + + /* find numbers needed to test */ + for(int i = 1; i <= 100; i++) + if(tabel[i]) + for(int j = i; j > 1; ) + { + /* calculate for one step */ + if(j % 2) j = (3 * j + 1) / 2; + else j /= 2; + + /* see if the new number is in given numbers */ + if(j <= 100 && tabel[j]) + { + tabel[j] = 0; /* 'covered' this number */ + K--; /* one less number not 'covered' */ + if(j < i) break; /* did this before, no need going on */ + } + } + + for(int i = 100; i >= 1; i--) + if(tabel[i] == 1) + printf("%d%c", i, --K ? ' ' : '\0'); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1006.md b/_posts/2018-05-03-b1006.md new file mode 100644 index 0000000..e5920ae --- /dev/null +++ b/_posts/2018-05-03-b1006.md @@ -0,0 +1,53 @@ +--- +layout: post +title: "1006. 换个格式输出整数 (15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

让我们用字母B来表示“百”、字母S表示“十”,用“12...n”来表示个位数字n(&lt10;),换个格式来输出任一个不超过3位的正整数。例如234应该被输出为BBSSS1234,因为它有2个“百”、3个“十”、以及个位的4。

+>

输入格式:每个测试输入包含1个测试用例,给出正整数n(&lt1000;)。

+>

输出格式:每个测试用例的输出占一行,用规定的格式输出n。

+> 输入样例1:
+> 234
+> 
+> 输出样例1:
+> BBSSS1234
+> 
+> 输入样例2:
+> 23
+> 
+> 输出样例2:
+> SS123
+> 
+>
+ +## 思路 + + +合理运用循环条件,可以写出简洁的代码。 + +题目限制整数的位数最多三位,那么可以提取三位上的数字就可以了。某一位为0,那么循环根本不会执行,因此也不需要写额外的判断语句。 + + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1006.c),欢迎交流 +```c +#include + +int main() +{ + int n; + scanf("%d", &n); + + for(int i = 0; i < n / 100; i++) putchar('B'); + for(int i = 0; i < n / 10 % 10; i++) putchar('S'); + for(int i = 0; i < n % 10; i++) putchar('1' + i); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1007.md b/_posts/2018-05-03-b1007.md new file mode 100644 index 0000000..2519f9d --- /dev/null +++ b/_posts/2018-05-03-b1007.md @@ -0,0 +1,80 @@ +--- +layout: post +title: "1007. 素数对猜想 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

让我们定义 dn 为:dn = pn+1 - pn,其中 pi 是第i个素数。显然有 d1=1 且对于n&gt1;有 dn 是偶数。“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。 +>

+>

现给定任意正整数N (< 105),请计算不超过N的满足猜想的素数对的个数。

+>

输入格式:每个测试输入包含1个测试用例,给出正整数N。

+>

输出格式:每个测试用例的输出占一行,不超过N的满足猜想的素数对的个数。

+> 输入样例:
+> 20
+> 
+> 输出样例:
+> 4
+> 
+>
+ +## 思路 + +初始化:100个素数里初始化便写入前两个2,3,从4开始验证,这样不影响边界情况(N=5之前没有孪生素数),避免了2这样没有更小的素数可供验证的情况,并且进入循环即可开始验证孪生素数。 + +**结果参考:** + +|N|孪生素数对数| +|:-:|:-:| +|1~4|0| +|20|4| +|100|8| +|1000|35| +|10000|205| +|100000|1224| + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1007.c),欢迎交流 +```c +#include + +int main() +{ + int N; + scanf("%d", &N); + + /* Record primality of three successive numbers starting from 2, 3, 4 */ + int iPrimeMinus2 = 1, iPrimeMinus1 = 1, iPrime; + int primes[100] = {2, 3}; /* Record the prime numbers before sqrt(10^5) */ + int twincount = 0; /* Count of twin primes */ + int primecount = 2; /* Count of prime numbers */ + + /* Start from 4 */ + for(int i = 4; i <= N; i++) + { + /* Test if i is a prime number */ + iPrime = 1; + for(int j = 0; iPrime && primes[j] * primes[j] <= i; j++) + if(i % primes[j] == 0) + iPrime = 0; + + /* If i is a prime number, record */ + if(iPrime) + { + if(primecount < 100) primes[primecount++] = i; + if(iPrimeMinus2 == 1) twincount++; /* a prime pair found */ + } + + /* Shift the primality flags to next numbers */ + iPrimeMinus2 = iPrimeMinus1; + iPrimeMinus1 = iPrime; + } + printf("%d", twincount); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1008.md b/_posts/2018-05-03-b1008.md new file mode 100644 index 0000000..c50e567 --- /dev/null +++ b/_posts/2018-05-03-b1008.md @@ -0,0 +1,54 @@ +--- +layout: post +title: "1008. 数组元素循环右移问题 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

一个数组A中存有N(N&gt0;)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(M>=0)个位置,即将A中的数据由(A0 A1……AN-1)变换为(AN-M …… AN-1 A0 A1……AN-M-1)(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?

+>

输入格式:每个输入包含一个测试用例,第1行输入N ( 1<=N<=100)、M(M>=0);第2行输入N个整数,之间用空格分隔。

+>

输出格式:在一行中输出循环右移M位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。

+> 输入样例:
+> 6 2
+> 1 2 3 4 5 6
+> 
+> 输出样例:
+> 5 6 1 2 3 4
+> 
+>
+ +## 思路 + + +很简单的一道题,一个需注意的点为M可能大于等于N,需要对N取余再进行接下来的操作。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1008.c),欢迎交流 +```c +#include + +int main() +{ + int N, M, numbers[100]; + + scanf("%d %d", &N, &M); + M %= N; /* M could be larger than N */ + + /* Read */ + for(int i = 0; i < N; i++) + scanf("%d", &numbers[i]); + + /* Print */ + for(int i = N - M; i < N; i++) /* Print N - M to N - 1 */ + printf("%d ", numbers[i]); + for(int i = 0; i < N - M - 1; i++) /* Print 0 to N - M - 2 */ + printf("%d ", numbers[i]); + printf("%d", numbers[N - M - 1]); /* Print N - M - 1, no blankspace */ + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1009.md b/_posts/2018-05-03-b1009.md new file mode 100644 index 0000000..8d12d37 --- /dev/null +++ b/_posts/2018-05-03-b1009.md @@ -0,0 +1,58 @@ +--- +layout: post +title: "1009. 说反话 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。

+>

输入格式:测试输入包含一个测试用例,在一行内给出总长度不超过80的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母(大小写有区分)组成的字符串,单词之间用1个空格分开,输入保证句子末尾没有多余的空格。

+>

输出格式:每个测试用例的输出占一行,输出倒序后的句子。

+> 输入样例:
+> Hello World Here I Come
+> 
+> 输出样例:
+> Come I Here World Hello
+> 
+>
+ +## 思路 + +只用一个字符串,从后向前搜索单词,依次输出。 + +下面的代码是不断修改过的结果,最后改成了一个很具有“C Bible”风格的代码(可能是这本书看多了(⊙﹏⊙)b)。只用很基本的函数,很多功能自己来实现,所以可读性就比较差——但是我觉得挺有意思的(\*^__^\*) + +**运行时间:** 我以为就80个字符,还不不到1毫秒就完成?结果PAT的结果都是20毫秒上下!之后我在自己的代码里用time.h测试了一下:还不到0.1毫秒 (〃>皿<) +(有些题我在本地的时间可是比PAT结果还要长的,搞不懂~~) + +**评:** 用高级语言的栈就轻松的多了(所以我觉得C++确实很适合PAT);为了将末尾的换行也跳过,我用了`isspace`,这样一来输入格式变得很宽松,任何空白字符分隔的单词都能接受。 + +**附:** [最初的代码](https://github.com/OliverLew/PAT/blob/045245fd75d9f1199c2bc634e6a5f7bf50770bf2/PATBasic/1009.c)。思路是一个单词一个单词地读,然后倒序地连接起来,使用string.h的函数。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1009.c),欢迎交流 +```c +#include +#include + +int main() +{ + char line[81], *p = line, *i; + fgets(line, 81, stdin); + + while(*++p); /* Go to the end of the string */ + while(p > line) /* Will break at the beginning of the string */ + { + while(isspace(*--p)) ; /* Find the end of a word */ + while(p > line && !isspace(*(p - 1))) p--; /* Find start of the word */ + for(i = p; *i && !isspace(*i); putchar(*i++)); /* Print the word */ + putchar(p == line ? '\0' : ' '); /* print blankspace if not at end */ + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1010.md b/_posts/2018-05-03-b1010.md new file mode 100644 index 0000000..e06d77e --- /dev/null +++ b/_posts/2018-05-03-b1010.md @@ -0,0 +1,55 @@ +--- +layout: post +title: "1010. 一元多项式求导 (25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

设计函数求一元多项式的导数。(注:xn(n为整数)的一阶导数为n*xn-1。)

+>

输入格式:以指数递降方式输入多项式非零项系数和指数(绝对值均为不超过1000的整数)。数字间以空格分隔。

+>

输出格式:以与输入相同的格式输出导数多项式非零项的系数和指数。数字间以空格分隔,但结尾不能有多余空格。注意“零多项式”的指数和系数都是0,但是表示为“0 0”。

+> 输入样例:
+> 3 4 -5 2 6 1 -2 0
+> 
+> 输出样例:
+> 12 3 -10 1 6 0
+> 
+>
+ +## 思路 + +这道题重点就是怎么处理各种各样的项的输出,如果思路清晰了就没有问题。 +- **读取:** 使用`scanf`一次读取两个`int`,读到`EOF`表示结束。 +P.S. 看到[这篇博客](http://blog.csdn.net/xtzmm1215/article/details/38408583)才学习到的(因此下面第一个代码不是我独立写出来的,当然看文中的描述,这个博主也是在别的地方看到的这个方法),后边附的第二个解法是我之前的方法,使用了`ungetc`,不过我觉得也算个方法吧~~只有换行的时候还是极好的。 +- **顺序:** 题目中没有任何提示多项式各项会按照何种顺序输入,保险起见,要写能接受任何顺序的多项式([例如牛客网这个例子](https://uploadfiles.nowcoder.com/images/20160606/344844_1465210468549_0B86F9634741D02D711276ABB9436874),话说这网站竟然给测试点内容)。 +- **输出"0 0"的情况:** 只有常数项的时候,需要输出0 0,常数项的特征就是指数为0。很多代码都用指数乘系数判断,其实系数为零、指数不为零的情况根本不存在嘛!指数为零就是充要了。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1010.c),欢迎交流 +```c +#include + +int main() +{ + int coef, index, count = 0; + + while(scanf("%d %d", &coef, &index) != EOF) + { + if(index) /* Constant terms result in zero */ + { + if(count++) putchar(' '); + printf("%d %d", coef * index, index - 1); + } + } + + /* Zero polynomial or constant */ + if(count == 0) + puts("0 0"); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1011.md b/_posts/2018-05-03-b1011.md new file mode 100644 index 0000000..d251753 --- /dev/null +++ b/_posts/2018-05-03-b1011.md @@ -0,0 +1,60 @@ +--- +layout: post +title: "1011. A+B和C (15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

给定区间[-231, 231]内的3个整数A、B和C,请判断A+B是否大于C。 +>

+>

+> 输入格式: +>

+>

输入第1行给出正整数T(<=10),是测试用例的个数。随后给出T组测试用例,每组占一行,顺序给出A、B和C。整数间以空格分隔。

+>

+> 输出格式: +>

+>

对每组测试用例,在一行中输出“Case #X: true”如果A+B>C,否则输出“Case #X: false”,其中X是测试用例的编号(从1开始)。

+> 输入样例:
+> 4
+> 1 2 3
+> 2 3 4
+> 2147483647 0 2147483646
+> 0 -2147483648 -2147483647
+> 
+> 输出样例:
+> Case #1: false
+> Case #2: true
+> Case #3: true
+> Case #4: false
+> 
+>
+ +## 思路 + +没有难度的题目,32位整型不够用,要用`long int`。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1011.c),欢迎交流 +```c +#include + +int main() +{ + int T; + long int A, B, C; + scanf("%d", &T); + + for(int i = 0; i < T; i++) + { + scanf("%ld %ld %ld", &A, &B, &C); + printf("Case #%d: %s\n", i + 1, A + B > C ? "true" : "false"); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1012.md b/_posts/2018-05-03-b1012.md new file mode 100644 index 0000000..a4d6989 --- /dev/null +++ b/_posts/2018-05-03-b1012.md @@ -0,0 +1,79 @@ +--- +layout: post +title: "1012. 数字分类 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
+>

给定一系列正整数,请按要求对数字进行分类,并输出以下5个数字:

+>
  • A1 = 能被5整除的数字中所有偶数的和; +>
  • A2 = 将被5除后余1的数字按给出顺序进行交错求和,即计算n1-n2+n3-n4...; +>
  • A3 = 被5除后余2的数字的个数; +>
  • A4 = 被5除后余3的数字的平均数,精确到小数点后1位; +>
  • A5 = 被5除后余4的数字中最大数字。 +> +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例。每个测试用例先给出一个不超过1000的正整数N,随后给出N个不超过1000的待分类的正整数。数字间以空格分隔。

    +>

    +> 输出格式: +>

    +>

    对给定的N个正整数,按题目要求计算A1~A5并在一行中顺序输出。数字间以空格分隔,但行末不得有多余空格。

    +>

    若其中某一类数字不存在,则在相应位置输出“N”。

    +> 输入样例1:
    +> 13 1 2 3 4 5 6 7 8 9 10 20 16 18
    +> 
    +> 输出样例1:
    +> 30 11 2 9.7 9
    +> 
    +> 输入样例2:
    +> 8 1 2 4 5 6 7 9 16
    +> 
    +> 输出样例2:
    +> N 11 2 N 9
    +> 
    +>
  • + +## 思路 + +每次读一个数字,根据对5的余数进行不同的处理即可。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1012.c),欢迎交流 +```c +#include + +int main() +{ + int count; + int A1 = 0, A2 = 0, A3 = 0, A4 = 0, A5 = 0; + int A2flag = 0, A4count = 0; + + scanf("%d", &count); + for(int i = 0, n; i < count; i++) + { + scanf("%d", &n); + switch(n % 5) + { + case 0: A1 += n % 2 ? 0 : n; break; + case 1: A2flag = A2flag == 1 ? -1 : 1; A2 += A2flag * n; break; + case 2: A3 ++; break; + case 3: A4 += n; A4count ++; break; + case 4: A5 = n > A5? n : A5; break; + } + } + + if(A1 == 0) printf("N "); else printf("%d ", A1); + if(A2flag == 0) printf("N "); else printf("%d ", A2); + if(A3 == 0) printf("N "); else printf("%d ", A3); + if(A4 == 0) printf("N "); else printf("%.1f ", A4 * 1.0 / A4count); + if(A5 == 0) printf("N"); else printf("%d", A5); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1013.md b/_posts/2018-05-03-b1013.md new file mode 100644 index 0000000..02ee496 --- /dev/null +++ b/_posts/2018-05-03-b1013.md @@ -0,0 +1,73 @@ +--- +layout: post +title: "1013. 数素数 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 令Pi表示第i个素数。现任给两个正整数M <= N <= 104,请输出PM到PN的所有素数。

    +>

    +> 输入格式: +>

    +>

    输入在一行中给出M和N,其间以空格分隔。

    +>

    +> 输出格式: +>

    +>

    输出从PM到PN的所有素数,每10个数字占1行,其间以空格分隔,但行末不得有多余空格。

    +> 输入样例:
    +> 5 27
    +> 
    +> 输出样例:
    +> 11 13 17 19 23 29 31 37 41 43
    +> 47 53 59 61 67 71 73 79 83 89
    +> 97 101 103
    +> 
    +>
    + +## 思路 + +也没有什么难度,注意下什么时候开始输出,什么时候换行。 + +和[1007. 素数对猜想 (20) (C语言实现)](http://www.jianshu.com/p/728c2602d104)一样,验证素数的时候,利用前面已经算出的素数,能够将验证次数降到最低。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1013.c),欢迎交流 +```c +#include + +int main() +{ + + int M, N; + scanf("%d %d", &M, &N); + int primes[10000]; + + for(int n = 2, count = 0; count < N; n++) + { + /* Check if n is prime number */ + int iprime = 1; + for(int j = 0; count > 0 && primes[j] * primes[j] <= n; j++) + if(n % primes[j] == 0) + iprime = 0; + + /* Record */ + if(iprime) + primes[count++] = n; + } + + /* Print */ + for(int i = M; i < N; i++) + { + printf("%d", primes[i - 1]); + printf((i - M + 1) % 10 ? " " : "\n"); + } + printf("%d", primes[N - 1]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1014.md b/_posts/2018-05-03-b1014.md new file mode 100644 index 0000000..1ec34af --- /dev/null +++ b/_posts/2018-05-03-b1014.md @@ -0,0 +1,94 @@ +--- +layout: post +title: "1014. 福尔摩斯的约会 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    大侦探福尔摩斯接到一张奇怪的字条:“我们约会吧! +> 3485djDkxh4hhGE +> 2984akDfkkkkggEdsb +> s&hgsfdk; +> d&Hyscvnm;”。大侦探很快就明白了,字条上奇怪的乱码实际上就是约会的时间“星期四 14:04”,因为前面两字符串中第1对相同的大写英文字母(大小写有区分)是第4个字母'D',代表星期四;第2对相同的字符是'E',那是第5个英文字母,代表一天里的第14个钟头(于是一天的0点到23点由数字0到9、以及大写字母A到N表示);后面两字符串第1对相同的英文字母's'出现在第4个位置(从0开始计数)上,代表第4分钟。现给定两对字符串,请帮助福尔摩斯解码得到约会的时间。

    +>

    +> 输入格式: +>

    +>

    输入在4行中分别给出4个非空、不包含空格、且长度不超过60的字符串。 +>

    +>

    +> 输出格式: +>

    +>

    在一行中输出约会的时间,格式为“DAY HH:MM”,其中“DAY”是某星期的3字符缩写,即MON表示星期一,TUE表示星期二,WED表示星期三,THU表示星期四,FRI表示星期五,SAT表示星期六,SUN表示星期日。题目输入保证每个测试存在唯一解。

    +> 输入样例:
    +> 3485djDkxh4hhGE 
    +> 2984akDfkkkkggEdsb 
    +> s&hgsfdk; 
    +> d&Hyscvnm;
    +> 
    +> 输出样例:
    +> THU 14:04
    +> 
    +>
    + +## 思路 + +这道题因为题目叙述的模糊性让好多人尝试了很多次才成功,比如“第二对相同的字符”的描述就很不好理解,其实如果题目叙述更加明确一点,很多人就不用遭罪了 + +- 确定星期:前两个字符串中位置相同且字符相同的第一个大写字母,并且应处于[A-G] +- 确定小时:接星期的字符向后查找,位置相同的下一个相同的字符,并且处于[0-9A-N] +- 确定分钟:后两个字符串中位于相同位置且相同的第一个字母 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1014.c),欢迎交流 +```c +#include +#include + +int main() +{ + char str1[61], str2[61], str3[61], str4[61]; + scanf("%s %s %s %s", str1, str2, str3, str4); + + /* Find day, same char from [A-G] and same position in frist two lines */ + int DAY; + char *weekdays[] = {"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"}; + for(DAY = 0; str1[DAY] && str2[DAY]; DAY++) + if(str1[DAY] == str2[DAY] && str1[DAY] >= 'A' && str1[DAY] <= 'G') + { + printf("%s", weekdays[str1[DAY] - 'A']); + break; + } + + /* Find hour, picking up from DAY, same character and position, [A-N|0-9] */ + int HH; + for(HH = DAY + 1; str1[HH] && str2[HH]; HH++) + if(str1[HH] == str2[HH]) + { + if(str1[HH] >= 'A' && str1[HH] <= 'N') + { + printf(" %02d", str1[HH] - 'A' + 10); + break; + } + if(isdigit(str1[HH])) + { + printf(" %02d", str1[HH] - '0'); + break; + } + } + + /* Find minute, same alphabet character from last two lines */ + int MM; + for(MM = 0; str3[MM] && str4[MM]; MM++) + if(str3[MM] == str4[MM] && isalpha(str3[MM])) + { + printf(":%02d", MM); + break; + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1015.md b/_posts/2018-05-03-b1015.md new file mode 100644 index 0000000..55a9a83 --- /dev/null +++ b/_posts/2018-05-03-b1015.md @@ -0,0 +1,92 @@ +--- +layout: post +title: "1015. 德才论 (25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 宋代史学家司马光在《资治通鉴》中有一段著名的“德才论”:“是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之小人。凡取人之术,苟不得圣人,君子而与之,与其得小人,不若得愚人。” +>

    +>

    +> 现给出一批考生的德才分数,请根据司马光的理论给出录取排名。 +>

    +>

    +> 输入格式: +>

    +>

    输入第1行给出3个正整数,分别为:N(<=105),即考生总数;L(>=60),为录取最低分数线,即德分和才分均不低于L的考生才有资格被考虑录取;H(<100),为优先录取线——德分和才分均不低于此线的被定义为“才德全尽”,此类考生按德才总分从高到低排序;才分不到但德分到线的一类考生属于“德胜才”,也按总分排序,但排在第一类考生之后;德才分均低于H,但是德分不低于才分的考生属于“才德兼亡”但尚有“德胜才”者,按总分排序,但排在第二类考生之后;其他达到最低线L的考生也按总分排序,但排在第三类考生之后。 +>

    +>

    随后N行,每行给出一位考生的信息,包括:准考证号、德分、才分,其中准考证号为8位整数,德才分为区间[0, 100]内的整数。数字间以空格分隔。

    +>
    + +## 思路 + +利用`qsort`函数进行排序,按照题目叙述编写比较函数。 + +这道题用时略长,最慢的测试点一直是100ms左右,什么时候想起来去做一个性能测试 ╮(╯▽╰)╭ + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1015.c),欢迎交流 +```c +#include +#include + +typedef struct _Student{ + int ID; + int D; /** de2 : virtue */ + int C; /** cai2: ability */ + int rank; + int sum; /** sum = D + C */ +}sStudent, *Student; + +/* larger the number, higher the rank */ +int rank(Student s, int H, int L) +{ + if(s->D < L || s->C < L) return 0; /* failed */ + else if(s->D >= H && s->C >= H) return 4; /* best */ + else if(s->D >= H) return 3; /* second */ + else if(s->D >= s->C) return 2; /* third */ + else return 1; /* fourth */ +} + +int comp(const void *a, const void *b) +{ + Student s1 = *(Student*)a; + Student s2 = *(Student*)b; + + if(s1->rank != s2->rank) return s1->rank - s2->rank; + else if(s1->sum != s2->sum) return s1->sum - s2->sum; + else if(s1->D != s2->D) return s1->D - s2->D; + else if(s1->ID != s2->ID) return s2->ID - s1->ID; + else return 0; +} + +int main() +{ + int N, L, H, M = 0; + Student students[100000] = {0}; + sStudent buffer[100000]; + + scanf("%d %d %d", &N, &L, &H); + for(int i = 0; i < N; i++) + { + Student s = buffer + i; + scanf("%d %d %d", &s->ID, &s->D, &s->C); + s->sum = s->D + s->C; + if((s->rank = rank(s, H, L)) != 0) /* record if passed */ + students[M++] = s; + } + + qsort(students, M, sizeof(Student), comp); + + printf("%d\n", M); + for(int i = M - 1; i >= 0; i--) + printf("%d %d %d\n", students[i]->ID, students[i]->D, students[i]->C); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1016.md b/_posts/2018-05-03-b1016.md new file mode 100644 index 0000000..0120ebf --- /dev/null +++ b/_posts/2018-05-03-b1016.md @@ -0,0 +1,64 @@ +--- +layout: post +title: "1016. 部分A+B (15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    正整数A的“DA(为1位整数)部分”定义为由A中所有DA组成的新整数PA。例如:给定A = 3862767,DA = 6,则A的“6部分”PA是66,因为A中有2个6。

    +>

    现给定A、DA、B、DB,请编写程序计算PA + PB。

    +>

    +> 输入格式: +>

    +>

    输入在一行中依次给出A、DA、B、DB,中间以空格分隔,其中0 < A, B < 1010。

    +>

    +> 输出格式: +>

    +>

    在一行中输出PA + PB的值。 +>

    +> 输入样例1:
    +> 3862767 6 13530293 3
    +> 
    +> 输出样例1:
    +> 399
    +> 
    +> 输入样例2:
    +> 3862767 1 13530293 8
    +> 
    +> 输出样例2:
    +> 0
    +> 
    +>
    + +## 思路 + +2^31 < 2*10^10,因此要用`long int`。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1016.c),欢迎交流 +```c +#include + +long Dpart(long A, int D_A) +{ + long P_A; + for(P_A = 0; A; A /= 10) + if(A % 10 == D_A) + P_A = P_A * 10 + D_A; + return P_A; +} + +int main() +{ + long A, B; + int D_A, D_B; + scanf("%ld %d %ld %d", &A, &D_A, &B, &D_B); + printf("%ld", Dpart(A, D_A) + Dpart(B, D_B)); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1017.md b/_posts/2018-05-03-b1017.md new file mode 100644 index 0000000..9919674 --- /dev/null +++ b/_posts/2018-05-03-b1017.md @@ -0,0 +1,67 @@ +--- +layout: post +title: "1017. A除以B (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 本题要求计算A/B,其中A是不超过1000位的正整数,B是1位正整数。你需要输出商数Q和余数R,使得A = B * Q + R成立。

    +>

    +> 输入格式: +>

    +>

    输入在1行中依次给出A和B,中间以1空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    在1行中依次输出Q和R,中间以1空格分隔。 +>

    +> 输入样例:
    +> 123456789050987654321 7
    +> 
    +> 输出样例:
    +> 17636684150141093474 3
    +> 
    +>
    + +## 思路 + +就是模拟一个手算除法的过程。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1017.c),欢迎交流 +```c +#include + +/* read 2 digits from highest digit of A, do manual division, get the quotient + and remainder. Read one more digit, combine this with the last remainder to +get a new 2-digits number. Do this until read to the end of A */ + +int main() +{ + int B; + char A[1001], *p = A; + scanf("%s %d", A, &B); + + /* the results are stored in A and B instead of printed out on-the-fly */ + int twodigit, remainder = 0; + for(int i = 0; A[i]; i ++) + { + twodigit = remainder * 10 + (A[i] - '0'); + A[i] = twodigit / B + '0'; + remainder = twodigit % B; + } + B = remainder; + + /* print */ + if(A[0] == '0' && A[1] != '\0') p++; + printf("%s %d", p, B); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1018.md b/_posts/2018-05-03-b1018.md new file mode 100644 index 0000000..3280f71 --- /dev/null +++ b/_posts/2018-05-03-b1018.md @@ -0,0 +1,92 @@ +--- +layout: post +title: "1018. 锤子剪刀布 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 大家应该都会玩“锤子剪刀布”的游戏:两人同时给出手势,胜负规则如图所示:

    +>
    +>

    +> 现给出两人的交锋记录,请统计双方的胜、平、负次数,并且给出双方分别出什么手势的胜算最大。 +>

    +>

    +> 输入格式: +>

    +>

    输入第1行给出正整数N(<=105),即双方交锋的次数。随后N行,每行给出一次交锋的信息,即甲、乙双方同时给出的的手势。C代表“锤子”、J代表“剪刀”、B代表“布”,第1个字母代表甲方,第2个代表乙方,中间有1个空格。 +>

    +>

    +> 输出格式: +>

    +>

    输出第1、2行分别给出甲、乙的胜、平、负次数,数字间以1个空格分隔。第3行给出两个字母,分别代表甲、乙获胜次数最多的手势,中间有1个空格。如果解不唯一,则输出按字母序最小的解。 +>

    +> 输入样例:
    +> 10
    +> C J
    +> J B
    +> C B
    +> B B
    +> B C
    +> C C
    +> C B
    +> J B
    +> B C
    +> J J
    +> 
    +> 输出样例:
    +> 5 3 2
    +> 2 3 5
    +> B B
    +> 
    +>
    + +## 思路 + +数数 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1018.c),欢迎交流 +```c +#include + +char max(int B, int C, int J) +{ + if(B >= C && B >= J) return 'B'; + if(C > B && C >= J) return 'C'; + /* J > B && J > C */ return 'J'; +} + +int main() +{ + int N; + char a, b; + int AwinB = 0, AwinC = 0, AwinJ = 0; + int BwinB = 0, BwinC = 0, BwinJ = 0; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf(" %c %c", &a, &b); /* Notice the space before every %c */ + if(a == 'B' && b == 'C') AwinB++; + if(a == 'C' && b == 'J') AwinC++; + if(a == 'J' && b == 'B') AwinJ++; + if(a == 'B' && b == 'J') BwinJ++; + if(a == 'C' && b == 'B') BwinB++; + if(a == 'J' && b == 'C') BwinC++; + } + + int Awin = AwinB + AwinC + AwinJ; + int Bwin = BwinB + BwinC + BwinJ; + int Tie = N - Awin - Bwin; + printf("%d %d %d\n", Awin, Tie, Bwin); + printf("%d %d %d\n", Bwin, Tie, Awin); + printf("%c %c", max(AwinB, AwinC, AwinJ), max(BwinB, BwinC, BwinJ)); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1019.md b/_posts/2018-05-03-b1019.md new file mode 100644 index 0000000..4466828 --- /dev/null +++ b/_posts/2018-05-03-b1019.md @@ -0,0 +1,93 @@ +--- +layout: post +title: "1019. 数字黑洞 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 给定任一个各位数字不完全相同的4位正整数,如果我们先把4个数字按非递增排序,再按非递减排序,然后用第1个数字减第2个数字,将得到一个新的数字。一直重复这样做,我们很快会停在有“数字黑洞”之称的6174,这个神奇的数字也叫Kaprekar常数。

    +>

    例如,我们从6767开始,将得到

    +>

    +> 7766 - 6677 = 1089
    +> 9810 - 0189 = 9621
    +> 9621 - 1269 = 8352
    +> 8532 - 2358 = 6174
    +> 7641 - 1467 = 6174
    +> ... ...

    +>

    现给定任意4位正整数,请编写程序演示到达黑洞的过程。

    +>

    +> 输入格式: +>

    +>

    输入给出一个(0, 10000)区间内的正整数N。 +>

    +>

    +> 输出格式: +>

    +>

    如果N的4位数字全相等,则在一行内输出“N - N = 0000”;否则将计算的每一步在一行内输出,直到6174作为差出现,输出格式见样例。注意每个数字按4位数格式输出。 +>

    +> 输入样例1:
    +> 6767
    +> 
    +> 输出样例1:
    +> 7766 - 6677 = 1089
    +> 9810 - 0189 = 9621
    +> 9621 - 1269 = 8352
    +> 8532 - 2358 = 6174
    +> 
    +> 输入样例2:
    +> 2222
    +> 
    +> 输出样例2:
    +> 2222 - 2222 = 0000
    +> 
    +>
    + +## 思路 + +也是比较简单,输入直接用`scanf("%d", N)`,然后写两个对4位以内整型排序和逆序的函数,输出的格式化字符串是`"%04d"`。从整型的角度处理比字符串处理要简洁一些。 + +使用`do{}while()`或者等价的方式,让过程至少输出一行,这样0000和6174不必进行特殊处理。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1019.c),欢迎交流 +```c +#include +#include + +int cmp(const void *a, const void *b) +{ + return *(int*)b - *(int*)a; +} + +int sort(int n) +{ + int digits[4] = {n/1000, n%1000/100, n%100/10, n%10}; + qsort(digits, 4, sizeof(int), cmp); + return digits[0] * 1000 + digits[1] * 100 + digits[2] * 10 + digits[3]; +} + +int reverse(int n) +{ + return n/1000 + n%1000/100 * 10 + n%100/10 * 100 + n%10 * 1000; +} + +int main() +{ + int N; + + scanf("%d", &N); + do + { + N = sort(N); + printf("%04d - %04d = %04d\n", N, reverse(N), N - reverse(N)); + N = N - reverse(N); + }while(N != 0 && N != 6174) ; + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1020.md b/_posts/2018-05-03-b1020.md new file mode 100644 index 0000000..41161fc --- /dev/null +++ b/_posts/2018-05-03-b1020.md @@ -0,0 +1,76 @@ +--- +layout: post +title: "1020. 月饼 (25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。 +>

    +>

    注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有3种月饼,其库存量分别为18、15、10万吨,总售价分别为75、72、45亿元。如果市场的最大需求量只有20万吨,那么我们最大收益策略应该是卖出全部15万吨第2种月饼、以及5万吨第3种月饼,获得 72 + 45/2 = 94.5(亿元)。

    +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例。每个测试用例先给出一个不超过1000的正整数N表示月饼的种类数、以及不超过500(以万吨为单位)的正整数D表示市场最大需求量。随后一行给出N个正数表示每种月饼的库存量(以万吨为单位);最后一行给出N个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。

    +>

    +> 输出格式: +>

    +>

    对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后2位。

    +> 输入样例:
    +> 3 20
    +> 18 15 10
    +> 75 72 45
    +> 
    +> 输出样例:
    +> 94.50
    +> 
    +>
    + +## 思路 + +每次找到未卖完的月饼中单价最高的,分情况讨论一下这种月饼的库存是否足够剩下的需求量。 + +更新:这道题迷迷糊糊就AC了,看了别人的文章才知道库存可能不够。。。(′`〃)。我通过的原因是每次卖出一种月饼,就将该月饼的售价改为0,这样时候找最高单价的时候就不会找到这种月饼了,这是初衷。不过由于售价改为0,如果库存不够(此时所有售价都为0),月饼还是在“卖”,但是没有收入(。・∀・)ノ゙,因此相当于变相地解决了库存不够的问题。(突然觉得自己好机智怎么办) + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1020.c),欢迎交流 +```c +#include + +int main() +{ + int N, max; + float D, Storage[1000], Total = 0, Price[1000]; + + scanf("%d %f", &N, &D); + for(int i = 0; i < N; i++) scanf("%f", Storage + i); + for(int i = 0; i < N; i++) scanf("%f", Price + i); + + while(D > 0) + { + max = 0; + for(int i = 0; i < N; i++) + if(Price[i] / Storage[i] > Price[max] / Storage[max]) + max = i; + + if(Storage[max] < D) + { + Total += Price[max]; + D -= Storage[max]; + Price[max] = 0; + } + else + { + Total += Price[max] * D / Storage[max]; + D = 0; + } + } + printf("%.2f", Total); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1021.md b/_posts/2018-05-03-b1021.md new file mode 100644 index 0000000..fbc90b0 --- /dev/null +++ b/_posts/2018-05-03-b1021.md @@ -0,0 +1,52 @@ +--- +layout: post +title: "1021. 个位数统计 (15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    给定一个k位整数N = dk-1*10k-1 + ... + d1*101 + d0 (0<=di<=9, i=0,...,k-1, dk-1>0),请编写程序统计每种不同的个位数字出现的次数。例如:给定N = 100311,则有2个0,3个1,和1个3。

    +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例,即一个不超过1000位的正整数N。

    +>

    +> 输出格式: +>

    +>

    对N中每一种不同的个位数字,以D:M的格式在一行中输出该位数字D及其在N中出现的次数M。要求按D的升序输出。

    +> 输入样例:
    +> 100311
    +> 
    +> 输出样例:
    +> 0:2
    +> 1:3
    +> 3:1
    +> 
    +>
    + +## 思路 + +属于最简单的一类题了。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1021.c),欢迎交流 +```c +#include + +int main() +{ + char c; + int count[10] = {0}; + while((c = getchar()) != '\n') + count[c - '0']++; + + for(int i = 0; i < 10; i++) if(count[i]) + printf("%d:%d\n", i, count[i]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1022.md b/_posts/2018-05-03-b1022.md new file mode 100644 index 0000000..7b93e4d --- /dev/null +++ b/_posts/2018-05-03-b1022.md @@ -0,0 +1,62 @@ +--- +layout: post +title: "1022. D进制的A+B (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 输入两个非负10进制整数A和B(<=230-1),输出A+B的D (1 < D <= 10)进制数。 +>

    +>

    +> 输入格式: +>

    +>

    输入在一行中依次给出3个整数A、B和D。 +>

    +>

    +> 输出格式: +>

    +>

    输出A+B的D进制数。 +>

    +> 输入样例:
    +> 123 456 8
    +> 
    +> 输出样例:
    +> 1103
    +> 
    +>
    + +## 思路 + + +A、B和A+B的范围在32位整型的范围内,因此用int就好不需要担心。 + +进制的转换也是很简单的,为了不使用数组,可以从最高位开始输出,这样就要先判断D进制下的位数。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1022.c),欢迎交流 +```c +#include + +int main() +{ + int A, B, D, Sum; + scanf("%d %d %d", &A, &B, &D); + Sum = A + B; + + /* calculate the bits of Sum */ + int power = 1; + /* use Sum / D >= power to avoid using long int */ + while(Sum / D >= power) power *= D; + + /* calculate D-base number. print them on-the-fly */ + for(; power > 0; Sum %= power, power /= D) + printf("%d", Sum / power); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1023.md b/_posts/2018-05-03-b1023.md new file mode 100644 index 0000000..6cf9c8b --- /dev/null +++ b/_posts/2018-05-03-b1023.md @@ -0,0 +1,61 @@ +--- +layout: post +title: "1023. 组个最小数 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 给定数字0-9各若干个。你可以以任意顺序排列这些数字,但必须全部使用。目标是使得最后得到的数尽可能小(注意0不能做首位)。例如:给定两个0,两个1,三个5,一个8,我们得到的最小的数就是10015558。

    +>

    现给定数字,请编写程序输出能够组成的最小的数。

    +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例。每个测试用例在一行中给出10个非负整数,顺序表示我们拥有数字0、数字1、……数字9的个数。整数间用一个空格分隔。10个数字的总个数不超过50,且至少拥有1个非0的数字。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出能够组成的最小的数。 +>

    +> 输入样例:
    +> 2 2 0 0 0 3 0 0 1 0
    +> 
    +> 输出样例:
    +> 10015558
    +> 
    +>
    + +## 思路 + +题目的输入提供了很大的方便,我使用了一个变量`zero`专门记录0的数量,在第一次非零数出现时,输出一个非零数,然后就把所有0都输出去,zero变成0,以后就不会再进入这个循环。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1023.c),欢迎交流 +```c +#include + +int main() +{ + int zero, nonzero; + + scanf("%d", &zero); + for(int i = 1; i < 10; i++) + { + scanf("%d", &nonzero); + while(nonzero--) + { + putchar('0' + i); + for(; zero; zero--) /* after the first non-zero, print all the zeros */ + putchar('0'); + } + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1024.md b/_posts/2018-05-03-b1024.md new file mode 100644 index 0000000..46db944 --- /dev/null +++ b/_posts/2018-05-03-b1024.md @@ -0,0 +1,81 @@ +--- +layout: post +title: "1024. 科学计数法 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    科学计数法是科学家用来表示很大或很小的数字的一种方便的方法,其满足正则表达式[+-][1-9]"."[0-9]+E[+-][0-9]+,即数字的整数部分只有1位,小数部分至少有1位,该数字及其指数部分的正负号即使对正数也必定明确给出。

    +>

    现以科学计数法的格式给出实数A,请编写程序按普通数字表示法输出A,并保证所有有效位都被保留。

    +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例,即一个以科学计数法表示的实数A。该数字的存储长度不超过9999字节,且其指数的绝对值不超过9999。

    +>

    +> 输出格式: +>

    +>

    对每个测试用例,在一行中按普通数字表示法输出A,并保证所有有效位都被保留,包括末尾的0。

    +> 输入样例1:
    +> +1.23400E-03
    +> 
    +> 输出样例1:
    +> 0.00123400
    +> 
    +> 输入样例2:
    +> -1.2E+10
    +> 
    +> 输出样例2:
    +> -12000000000
    +> 
    +>
    + +## 思路 + +很有意思的一个题目,让我们完成一个解析科学计数法的小功能。 + +读取的时候用到了一种格式化字符串```%[^...]```,这和`%s`类似,不过会终止于```[^]```里面的字符,而不是空白字符,利用这个可以简单的读取'E'前后的两个数。 + +解析时考虑三种情况: +- 指数大于(等于)0, + - 指数大于(等于)小数位数,末尾补0,不输出小数点, + - 指数小于小数位数,要在中间输出小数点, +- 指数小于0,在前面补0,小数点前移至第一个0后面。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1024.c),欢迎交流 +```c +#include + +int main() +{ + int exponent; /* the exponent part */ + char line[10000], *p = line; + scanf("%[^E]E%d", line, &exponent); + + if(*p++ == '-') putchar('-'); /* no print if it's '+' */ + if(exponent >= 0) /* print '.' later or add zeros in the end */ + { + putchar(*p); + for(p += 2; exponent; exponent--) /* print the integer part */ + putchar(*p ? *p++ : '0'); + if(*p) /* there is still fraction part */ + { + putchar('.'); + while(*p) putchar(*p++); + } + } + if(exponent < 0) /* add exponent zeros in the beginning */ + { + printf("0."); + for(exponent++; exponent; exponent++) /* add zeros */ + putchar('0'); + for(; *p; p++) if(*p != '.') putchar(*p); /* the rest */ + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1025.md b/_posts/2018-05-03-b1025.md new file mode 100644 index 0000000..8d91011 --- /dev/null +++ b/_posts/2018-05-03-b1025.md @@ -0,0 +1,108 @@ +--- +layout: post +title: "1025. 反转链表 (25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    给定一个常数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(<= 105)、以及正整数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
    +> 
    +>
    + +## 思路 + +这道题的一个“坑”:可能有孤立节点,整个链表的长度小于N。 + +- 读取每一个节点,存储到一个结构或int[3]数组; +- 将节点“链接”成为链表,在数组中排列,找到指向-1的节点说明链表已经结束; +- 反转链表。每K个一组,交换第i个和倒数第i个; +- 打印列表。反转时没有(也不必)修改每个节点的next,输出时应该相应输出下一个节点的address,最后一个节点的next输出-1。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1025.c),欢迎交流 +```c +#include +#include + +#define SWAPNODE(A, B) {Node temp = A; A = B; B = temp;} + +typedef struct node{ + int addr; + int data; + int next; +}node, *Node; + +int main() +{ + int A, N, K; + node nodes[100000] = {0}; + Node np[100000] = {0}, *p; + + /* read */ + scanf("%d %d %d", &A, &N, &K); + for(int i = 0; i < N; i++) + { + np[i] = nodes + i; + scanf("%d %d %d", &np[i]->addr, &np[i]->data, &np[i]->next); + } + + /* link the list */ + for(int i = 0; i < N; i++) + { + for(int j = i; j < N; j++) + if(np[j]->addr == (i ? np[i - 1]->next : A)) + { + SWAPNODE(np[i], np[j]); + break; + } + if(np[i]->next == -1) /* there could be useless nodes */ + N = i + 1; + } + + /* reverse the list */ + for(int i = 0; i < N / K; i++) + { + p = np + i * K; + for(int j = 0; j < K / 2; j++) + SWAPNODE(p[j], p[K - j - 1]); + } + + /* print the list */ + for(int i = 0; i < N - 1; i++) + printf("%05d %d %05d\n", np[i]->addr, np[i]->data, np[i + 1]->addr); + printf("%05d %d -1\n", np[N - 1]->addr, np[N - 1]->data); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1026.md b/_posts/2018-05-03-b1026.md new file mode 100644 index 0000000..17ecaf3 --- /dev/null +++ b/_posts/2018-05-03-b1026.md @@ -0,0 +1,55 @@ +--- +layout: post +title: "1026. 程序运行时间(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 要获得一个C语言程序的运行时间,常用的方法是调用头文件time.h,其中提供了clock()函数,可以捕捉从程序开始运行到clock()被调用时所耗费的时间。这个时间单位是clock tick,即“时钟打点”。同时还有一个常数CLK_TCK,给出了机器时钟每秒所走的时钟打点数。于是为了获得一个函数f的运行时间,我们只要在调用f之前先调用clock(),获得一个时钟打点数C1;在f执行完成后再调用clock(),获得另一个时钟打点数C2;两次获得的时钟打点数之差(C2-C1)就是f运行所消耗的时钟打点数,再除以常数CLK_TCK,就得到了以秒为单位的运行时间。 +>

    +>

    这里不妨简单假设常数CLK_TCK为100。现给定被测函数前后两次获得的时钟打点数,请你给出被测函数运行的时间。

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中顺序给出2个整数C1和C2。注意两次获得的时钟打点数肯定不相同,即C1 < C2,并且取值在[0, 107]。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出被测函数运行的时间。运行时间必须按照“hh:mm:ss”(即2位的“时:分:秒”)格式输出;不足1秒的时间四舍五入到秒。 +>

    +> 输入样例:
    +> 123 4577973
    +> 
    +> 输出样例:
    +> 12:42:59
    +> 
    +>
    + +## 思路 + +这道题也是最简单的一类题,考虑到四舍五入就行了。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1026.c),欢迎交流 +```c +#include + +int main() +{ + int C1, C2, T; + + scanf("%d %d", &C1, &C2); + T = (C2 - C1 + 50) / 100; /* rounding */ + printf("%02d:%02d:%02d", T/3600, T%3600/60, T%60); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1027.md b/_posts/2018-05-03-b1027.md new file mode 100644 index 0000000..ee8159e --- /dev/null +++ b/_posts/2018-05-03-b1027.md @@ -0,0 +1,79 @@ +--- +layout: post +title: "1027. 打印沙漏(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印

    +> *****
    +>  ***
    +>   *
    +>  ***
    +> *****
    +> 
    +>

    所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差2;符号数先从大到小顺序递减到1,再从小到大顺序递增;首尾符号数相等。

    +>

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

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行给出1个正整数N(<=1000)和一个符号,中间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。 +>

    +> 输入样例:
    +> 19 *
    +> 
    +> 输出样例:
    +> *****
    +>  ***
    +>   *
    +>  ***
    +> *****
    +> 2
    +> 
    +>
    + +## 思路 + +这简直就是一道纯数学题。 + +- M阶的沙漏需要用到的字符个数为2M^2-1; +- 明显沙漏关于第M行是对称的,用绝对值写会简洁一些。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1027.c),欢迎交流 +```c +#include + +#define ABS(X) ((X) >= 0 ? (X) : -(X)) + +int main() +{ + char c; + int N, M; + scanf("%d %c", &N, &c); + + for(M = 1; 2 * M * M - 1 <= N; M++) ; M--; /* determine the size */ + + /* draw the sandglass */ + for(int i = 0; i < 2 * M - 1; i ++){ + for(int j = 0; j < M - 1 - ABS(M - 1 - i); j++) putchar(' '); + for(int j = 0; j < 2 * ABS(M - 1 - i) + 1; j++) putchar(c); + putchar('\n'); + } + printf("%d", N - 2 * M * M + 1); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1028.md b/_posts/2018-05-03-b1028.md new file mode 100644 index 0000000..fd4a3da --- /dev/null +++ b/_posts/2018-05-03-b1028.md @@ -0,0 +1,82 @@ +--- +layout: post +title: "1028. 人口普查(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 某城镇进行人口普查,得到了全体居民的生日。现请你写个程序,找出镇上最年长和最年轻的人。 +>

    +>

    这里确保每个输入的日期都是合法的,但不一定是合理的——假设已知镇上没有超过200岁的老人,而今天是2014年9月6日,所以超过200岁的生日和未出生的生日都是不合理的,应该被过滤掉。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出正整数N,取值在(0, 105];随后N行,每行给出1个人的姓名(由不超过5个英文字母组成的字符串)、以及按“yyyy/mm/dd”(即年/月/日)格式给出的生日。题目保证最年长和最年轻的人没有并列。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中顺序输出有效生日的个数、最年长人和最年轻人的姓名,其间以空格分隔。 +>

    +> 输入样例:
    +> 5
    +> John 2001/05/12
    +> Tom 1814/09/06
    +> Ann 2121/01/30
    +> James 1814/09/05
    +> Steve 1967/11/20
    +> 
    +> 输出样例:
    +> 3 Tom John
    +> 
    +>
    + +## 思路 + + +使用三组字符串记录信息,最老的和最年轻的生日字符串初始化为`{'9'}`和`{'0'}`,这样就不需要对第一个合理输入进行特殊的处理。 + +这里使用了一个字符串记录生日和名字(前11位记录生日,后6位记录名字),完全可以用两个字符串分别记录,我只是发现这样可以少些几行代码(机智如我O(∩_∩)O~),性能几乎是一样的。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1028.c),欢迎交流 +```c +#include +#include + +int main() +{ + int N, count = 0; + /* store name and birthday in one string: "YYYY/MM/DD\0NAMES\0" */ + char cur[17], eldest[17] = {'9'}, youngest[17] = {'0'}; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%s %s", cur + 11, cur); + if(strcmp(cur, "1814/09/06") >= 0 && strcmp(cur, "2014/09/06") <= 0) + { + if(strcmp(cur, eldest) <= 0) + memcpy(eldest, cur, 17); + if(strcmp(cur, youngest) >= 0) + memcpy(youngest, cur, 17); + count++; + } + } + + if(count) + printf("%d %s %s", count, eldest + 11, youngest + 11); + else + printf("0"); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1029.md b/_posts/2018-05-03-b1029.md new file mode 100644 index 0000000..93dd92f --- /dev/null +++ b/_posts/2018-05-03-b1029.md @@ -0,0 +1,71 @@ +--- +layout: post +title: "1029. 旧键盘(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

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

    +>

    +> 输入格式: +>

    +>

    输入在2行中分别给出应该输入的文字、以及实际被输入的文字。每段文字是不超过80个字符的串,由字母A-Z(包括大、小写)、数字0-9、以及下划线“_”(代表空格)组成。题目保证2个字符串均非空。 +>

    +>

    +> 输出格式: +>

    +>

    +> 按照发现顺序,在一行中输出坏掉的键。其中英文字母只输出大写,每个坏键只输出一次。题目保证至少有1个坏键。

    +> 输入样例:
    +> 7_This_is_a_test
    +> _hs_s_a_es
    +> 
    +> 输出样例:
    +> 7TI
    +> 
    +>
    + +## 思路 + + +- 在第二行里查找第一行出现的字符,大小写视为同一个,没找到就是坏键。 +- 相同的坏键只输出一次,因此需要记录出现过的坏键。 +- 这道题有按发现顺序输出的要求,因此需要对第一行进行顺序遍历。 + +**代码实现:** + +对于这种记录字符出现之类的题目,我都是直接用int[128]数组,直接把字符的ASCII对应值当索引,简单粗暴。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1029.c),欢迎交流 +```c +#include +#include + +int main() +{ + int printed[128] = {0}; + char c, line[80]; + + gets(line); /* the newline was replaced with '\0' */ + while((c = getchar()) != '\n') + printed[toupper(c)]++; + + for(char *p = line; *p; p++) + { + c = toupper(*p); + if(printed[(int)c] == 0) + { + putchar(c); + printed[(int)c] = -1; + } + } + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1030.md b/_posts/2018-05-03-b1030.md new file mode 100644 index 0000000..2ae2e11 --- /dev/null +++ b/_posts/2018-05-03-b1030.md @@ -0,0 +1,86 @@ +--- +layout: post +title: "1030. 完美数列(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 给定一个正整数数列,和正整数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
    +> 
    +>
    + +## 思路 + +上了陈越、何钦铭老师的MOOC的同学(没上过也可能)应该记得一个“最大子列和”问题。陈越老师从O(N^3)的方案一直讲到O(N)的方案,其实就是思路的优化。这道题有很相似的特性。 +当然这道题应该没有O(N)的方案,因为这个需要排好序,所以至少是O(N log(N))。不过如果看排好序后的时间消耗,是可以达到O(N)的。 + +- 先对数组进行非递减排序。 +- 设置两个指针,分别代表完美数列的开头和结尾。 +- 每次我们都将结尾指向刚刚开始大于开头的一个数,二者距离就是目前完美数列的最长长度。 +- 开头指针向下进行遍历,这个时候结尾指针只需从原位置开始向下遍历即可,因为新的开头的数一定更大,那么结尾的数一定不会小于上次的值。 + +这样我们所用的时间就是开头和结尾分别一次遍历的时间,就是2N,完成O(N)的目标。 + +**代码实现:** + +题目给的数字都是小于10^9的,但是在判断M <= m \* p的时候,m \* p是可能大于32位整型范围的,要合理处理这个数字。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1030.c),欢迎交流 +```c +#include +#include + +int comp(const void *a, const void *b) +{ + return *(int*)a - *(int*)b; +} + +int main() +{ + int N, p, data[100000]; + int max = 0, first, last; + + scanf("%d %d", &N, &p); /* read */ + for(int i = 0; i < N; i++) + scanf("%d", data + i); + + qsort(data, N, sizeof(int), comp); /* sort */ + + for(first = 0, last = 0; last < N && max < N - first; first++) /* find */ + { + while(last < N && data[last] <= 1L * data[first] * p) + last++; + if(max < last - first) + max = last - first; + } + printf("%d", max); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1031.md b/_posts/2018-05-03-b1031.md new file mode 100644 index 0000000..6c09ad3 --- /dev/null +++ b/_posts/2018-05-03-b1031.md @@ -0,0 +1,90 @@ +--- +layout: post +title: "1031. 查验身份证(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下: +>

    +>

    首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};然后将计算的和对11取模得到值Z;最后按照以下关系对应Z值与校验码M的值:

    +>

    +> Z:0 1 2 3 4 5 6 7 8 9 10
    +> M:1 0 X 9 8 7 6 5 4 3 2 +>

    +>

    现在给定一些身份证号码,请你验证校验码的有效性,并输出有问题的号码。

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出正整数N(<= 100)是输入的身份证号码的个数。随后N行,每行给出1个18位身份证号码。 +>

    +>

    +> 输出格式: +>

    +>

    +> 按照输入的顺序每行输出1个有问题的身份证号码。这里并不检验前17位是否合理,只检查前17位是否全为数字且最后1位校验码计算准确。如果所有号码都正常,则输出“All passed”。 +>

    +> 输入样例1:
    +> 4
    +> 320124198808240056
    +> 12010X198901011234
    +> 110108196711301866
    +> 37070419881216001X
    +> 
    +> 输出样例1:
    +> 12010X198901011234
    +> 110108196711301866
    +> 37070419881216001X
    +> 
    +> 输入样例2:
    +> 2
    +> 320124198808240056
    +> 110108196711301862
    +> 
    +> 输出样例2:
    +> All passed
    +> 
    +>
    + +## 思路 + +也是比较简单的题目,检验前17个字符是否全是数字,和第18个字符和前17个字符是否相符。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1031.c),欢迎交流 +```c +#include + +int main() +{ + int N; + int weight[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; + char ZtoM[] = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}; + char ID[19]; + + scanf("%d", &N); + int d, sum, count = 0; /* index, weighted sum and count for legal IDs */ + for(int i = 0; i < N; i++) + { + scanf("%s", ID); + + for(d = 0, sum = 0; d < 17 && ID[d] >= '0' && ID[d] <= '9'; d++) + sum += (ID[d] - '0') * weight[d]; + + if(d == 17 && ID[17] == ZtoM[sum % 11]) /* legal ID */ + count++; + else /* illegal ID */ + puts(ID); + } + if(count == N) + puts("All passed"); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1032.md b/_posts/2018-05-03-b1032.md new file mode 100644 index 0000000..9eb0d1b --- /dev/null +++ b/_posts/2018-05-03-b1032.md @@ -0,0 +1,69 @@ +--- +layout: post +title: "1032. 挖掘机技术哪家强(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 为了用事实说明挖掘机技术到底哪家强,PAT组织了一场挖掘机技能大赛。现请你根据比赛结果统计出技术最强的那个学校。

    +>

    +> 输入格式: +>

    +>

    +> 输入在第1行给出不超过105的正整数N,即参赛人数。随后N行,每行给出一位参赛者的信息和成绩,包括其所代表的学校的编号(从1开始连续编号)、及其比赛成绩(百分制),中间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中给出总得分最高的学校的编号、及其总分,中间以空格分隔。题目保证答案唯一,没有并列。 +>

    +> 输入样例:
    +> 6
    +> 3 65
    +> 2 80
    +> 1 100
    +> 2 70
    +> 3 40
    +> 3 0
    +> 
    +> 输出样例:
    +> 2 150
    +> 
    +>
    + +## 思路 + +好一道有时代感的题目~ + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1032.c),欢迎交流 +```c +#include + +int main() +{ + int N, iSchool, score, imax = 0; + scanf("%d", &N); + + int schools[100000] = {0}; + for(int i = 0; i < N; i++) + { + scanf("%d %d", &iSchool, &score); + schools[iSchool - 1] += score; + } + + for(int i = 0; i < N; i++) + if(schools[i] > schools[imax]) + imax = i; + + printf("%d %d", imax + 1, schools[imax]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1033.md b/_posts/2018-05-03-b1033.md new file mode 100644 index 0000000..a273549 --- /dev/null +++ b/_posts/2018-05-03-b1033.md @@ -0,0 +1,69 @@ +--- +layout: post +title: "1033. 旧键盘打字(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现。现在给出应该输入的一段文字、以及坏掉的那些键,打出的结果文字会是怎样? +>

    +>

    +> 输入格式: +>

    +>

    输入在2行中分别给出坏掉的那些键、以及应该输入的文字。其中对应英文字母的坏键以大写给出;每段文字是不超过105个字符的串。可用的字符包括字母[a-z, A-Z]、数字0-9、以及下划线“_”(代表空格)、“,”、“.”、“-”、“+”(代表上档键)。题目保证第2行输入的文字串非空。 +>

    +>

    注意:如果上档键坏掉了,那么大写的英文字母无法被打出。 +> +>

    +> 输出格式: +>

    +>

    +> 在一行中输出能够被打出的结果文字。如果没有一个字符能被打出,则输出空行。

    +> 输入样例:
    +> 7+IE.
    +> 7_This_is_a_test.
    +> 
    +> 输出样例:
    +> _hs_s_a_tst
    +> 
    +>

    + +## 思路 + +这道就是1029题的逆向版,然后复杂了点而已。(但是代码更少了是什么鬼) + +- 读取:在记录坏键的时候很多种键,但是都是ASCII字符,因此用一个int[128]数组记录就好了,一视同仁,简单粗暴。 +- 输出:第二行是不会有'+'符号的,因此除了大写字母要特殊判断,只要这个键没有坏(数组没有记录),不管是哪一类符号,输出就行了。 +- 使用边读取边处理的方式,空间占用降到最低。 +- `toupper`函数对非字母字符会返回参数本身,因此记录/查找的时候不必区分字符类型,全都转换成大写。 + +这种方法比有多少种字符就创建多大的数组去记录要方便的多,代码量极少。 + +**注意**:第一行可能为空,使用scanf这样跳过空白字符的函数就不行了。我一开始就用的逐字符读取的方法,每次读到换行,一直都没有发觉这一点…… + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1033.c),欢迎交流 +```c +#include +#include + +int main() +{ + char c; + int bad[128] = {0}; /* record keys are broken or not */ + + while((c = getchar()) != '\n') /* read broken keys */ + bad[toupper(c)] = 1; + + while((c = getchar()) != '\n') /* read string and print */ + if(!bad[toupper(c)] && !(isupper(c) && bad['+'])) + putchar(c); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1034.md b/_posts/2018-05-03-b1034.md new file mode 100644 index 0000000..74e7522 --- /dev/null +++ b/_posts/2018-05-03-b1034.md @@ -0,0 +1,120 @@ +--- +layout: post +title: "1034. 有理数四则运算(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 本题要求编写程序,计算2个有理数的和、差、积、商。

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中按照“a1/b1 a2/b2”的格式给出两个分数形式的有理数,其中分子和分母全是整型范围内的整数,负号只可能出现在分子前,分母不为0。 +>

    +>

    +> 输出格式: +>

    +>

    +> 分别在4行中按照“有理数1 运算符 有理数2 = 结果”的格式顺序输出2个有理数的和、差、积、商。注意输出的每个有理数必须是该有理数的最简形式“k a/b”,其中k是整数部分,a/b是最简分数部分;若为负数,则须加括号;若除法分母为0,则输出“Inf”。题目保证正确的输出中没有超过整型范围的整数。 +>

    +> 输入样例1:
    +> 2/3 -4/2
    +> 
    +> 输出样例1:
    +> 2/3 + (-2) = (-1 1/3)
    +> 2/3 - (-2) = 2 2/3
    +> 2/3 * (-2) = (-1 1/3)
    +> 2/3 / (-2) = (-1/3)
    +> 
    +> 输入样例2:
    +> 5/3 0/6
    +> 
    +> 输出样例2:
    +> 1 2/3 + 0 = 1 2/3
    +> 1 2/3 - 0 = 1 2/3
    +> 1 2/3 * 0 = 0
    +> 1 2/3 / 0 = Inf
    +> 
    +>
    + +## 思路 + +这道题的注意点就是分数的约分和表示。 + +- 题目限定了输入输出都不超过整型范围,那么当我们计算两个分数的加减乘除时,分子分母就应该不超过长整形范围: + - 极限情况就是两者之和/之差的分子为![](http://latex.codecogs.com/svg.latex?a_1\times b_2+a_2\times b_1\le 2\times (2^{31}-1)^2<2^{63}-1) +- 输出形式共有3种: + - 假分数,输出整数部分和真分数部分; + - 真分数,只输出真分数部分; + - 整数,只输出整数部分。 +- 在判断输出的正负号时,如果分子分母还没有约分,那么小心使用两者之积来判断符号,因为已经溢出长整形范围。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1034.c),欢迎交流 +```c +#include + +/* Both parameters take positive value */ +long calcgcd(long a, long b) +{ + long r; + while((r = a % b)) + { + a = b; + b = r; + } + return b; +} + +/* print a fraction number, giving the numerator and dominator */ +void printfrac(long n, long d) +{ + if(d == 0) { printf("Inf"); return; } + + /* record the sign and make them positive */ + int inegative = 1; + if(n < 0) { n = -n; inegative *= -1; } + if(d < 0) { d = -d; inegative *= -1; } + + /* reduce the fraction */ + long gcd = calcgcd(n, d); + n /= gcd; + d /= gcd; + + /* print */ + if(inegative == -1) printf("(-"); + if(n / d && n % d) printf("%ld %ld/%ld", n / d, n % d, d); /* mixed fractions */ + else if(n % d) printf("%ld/%ld", n % d, d); /* proper fractions */ + else printf("%ld", n / d); /* integers */ + if(inegative == -1) printf(")"); +} + +int main() +{ + long a1, b1, a2, b2; + scanf("%ld/%ld %ld/%ld", &a1, &b1, &a2, &b2); + + char op[4] = {'+', '-', '*', '/'}; + for(int i = 0; i < 4; i++) + { + printfrac(a1, b1); printf(" %c ", op[i]); + printfrac(a2, b2); printf(" = "); + switch(op[i]) + { + case '+': printfrac(a1 * b2 + a2 * b1, b1 * b2); break; + case '-': printfrac(a1 * b2 - a2 * b1, b1 * b2); break; + case '*': printfrac(a1 * a2, b1 * b2); break; + case '/': printfrac(a1 * b2, b1 * a2); break; + } + printf("\n"); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1035.md b/_posts/2018-05-03-b1035.md new file mode 100644 index 0000000..99550c6 --- /dev/null +++ b/_posts/2018-05-03-b1035.md @@ -0,0 +1,112 @@ +--- +layout: post +title: "1035. 插入与归并(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    根据维基百科的定义:

    +>

    插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。

    +>

    +> 归并排序进行如下迭代操作:首先将原始序列看成N个只包含1个元素的有序子序列,然后每次迭代归并两个相邻的有序子序列,直到最后只剩下1个有序的序列。

    +>

    现给定原始序列和由某排序算法产生的中间序列,请你判断该算法究竟是哪种排序算法?

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出正整数N (<=100);随后一行给出原始序列的N个整数;最后一行给出由某排序算法产生的中间序列。这里假设排序的目标序列是升序。数字间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +> 首先在第1行中输出“Insertion Sort”表示插入排序、或“Merge Sort”表示归并排序;然后在第2行中输出用该排序算法再迭代一轮的结果序列。题目保证每组测试的结果是唯一的。数字间以空格分隔,且行末不得有多余空格。 +>

    +>

    +> 输入样例1:
    +> 10
    +> 3 1 2 8 7 5 9 4 6 0
    +> 1 2 3 7 8 5 9 4 6 0
    +> 
    +> 输出样例1:
    +> Insertion Sort
    +> 1 2 3 5 7 8 9 4 6 0
    +> 
    +> 输入样例2:
    +> 10
    +> 3 1 2 8 7 5 9 4 0 6
    +> 1 3 2 8 5 7 4 9 0 6
    +> 
    +> 输出样例2:
    +> Merge Sort
    +> 1 2 3 8 4 5 7 9 0 6
    +> 
    +>
    + +## 思路 + +这道题是我唯一看了几乎网上所有热门博客都没有发现自己错在哪的一道题,提交了10遍啊~差点绝望。最后乖乖地按照普遍做法改写了代码,终于过了。 + +题中说 +> 题目保证每组测试的结果是唯一的。 + +这应该有两点保证: +- 一定能区分插入排序和归并排序 +- 一定能确定排序的步数,不会产生某一步排序没有任何改变的情况 + +判断哪种排序方法 +- 插入排序: +从后向前找到和原始数组不同的元素,验证此元素之前是否为排好序的,如果是排好的,则是插入排序,下一步将第一个相同的元素插入排序 +- 归并排序: +从最初的数组模拟一步一步的归并,直至和中间序列相同,再向下进行一步 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1035.c),欢迎交流 +```c +#include +#include + +int comp(const void *a, const void *b) +{ + return *(int*)a - *(int*)b; +} + +int main() +{ + int N, origin[100], halfsort[100], i, j, length; + scanf("%d", &N); + for(int i = 0; i < N; i++) scanf("%d", origin + i); + for(int i = 0; i < N; i++) scanf("%d", halfsort + i); + + /* if it is insertion sort, return sorted length if yes, zero otherwise */ + for(i = 0; i < N - 1 && halfsort[i] <= halfsort[i + 1]; i++) ; + for(length = ++i; i < N && halfsort[i] == origin[i]; i++) ; + length = i == N ? length + 1 : 0; + + if(length) /* insertion sort */ + { + puts("Insertion Sort"); + qsort(origin, length, sizeof(int), comp); + } + else /* merge sort, operate on the original array */ + { + puts("Merge Sort"); + for(length = 1, i = 0; i < N && length <= N; length *= 2) + { + /* i == N means identical, also breaks the outer 'for' loop */ + for(i = 0; i < N && origin[i] == halfsort[i]; i++) ; + for(j = 0; j < N / length; j++) + qsort(origin + j * length, length, sizeof(int), comp); + qsort(origin + j * length, N % length, sizeof(int), comp); + } + } + + for(int i = 0; i < N; i++) + printf("%d%c", origin[i], i == N - 1 ? '\n' : ' '); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1036.md b/_posts/2018-05-03-b1036.md new file mode 100644 index 0000000..99f1e1c --- /dev/null +++ b/_posts/2018-05-03-b1036.md @@ -0,0 +1,69 @@ +--- +layout: post +title: "1036. 跟奥巴马一起编程(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统。2014年底,为庆祝“计算机科学教育周”正式启动,奥巴马编写了很简单的计算机代码:在屏幕上画一个正方形。现在你也跟他一起画吧! +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中给出正方形边长N(3<=N<=20)和组成正方形边的某种字符C,间隔一个空格。 +>

    +>

    +> 输出格式: +>

    +>

    +> 输出由给定字符C画出的正方形。但是注意到行间距比列间距大,所以为了让结果看上去更像正方形,我们输出的行数实际上是列数的50%(四舍五入取整)。

    +> 输入样例:
    +> 10 a
    +> 
    +> 输出样例:
    +> aaaaaaaaaa
    +> a        a
    +> a        a
    +> a        a
    +> aaaaaaaaaa
    +> 
    +>
    + +## 思路 + +也很简单,和那个画沙漏的差不多。 + +边界一圈画所给字符,里面画空格。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1036.c),欢迎交流 +```c +#include + +int main() +{ + int N; + char c; + scanf("%d %c", &N, &c); + + for(int i = 0; i < (N + 1) / 2; i++) + { + for(int j = 0; j < N; j++) + { + if(i == 0 || i == (N - 1) / 2 || j == 0 || j == N - 1) + putchar(c); + else + putchar(' '); + } + putchar('\n'); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1037.md b/_posts/2018-05-03-b1037.md new file mode 100644 index 0000000..15dd684 --- /dev/null +++ b/_posts/2018-05-03-b1037.md @@ -0,0 +1,63 @@ +--- +layout: post +title: "1037. 在霍格沃茨找零钱(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 如果你是哈利·波特迷,你会知道魔法世界有它自己的货币系统 —— 就如海格告诉哈利的:“十七个银西可(Sickle)兑一个加隆(Galleon),二十九个纳特(Knut)兑一个西可,很容易。”现在,给定哈利应付的价钱P和他实付的钱A,你的任务是写一个程序来计算他应该被找的零钱。 +>

    +>

    +> 输入格式: +>

    +>

    输入在1行中分别给出P和A,格式为“Galleon.Sickle.Knut”,其间用1个空格分隔。这里Galleon是[0, 107]区间内的整数,Sickle是[0, 17)区间内的整数,Knut是[0, 29)区间内的整数。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中用与输入同样的格式输出哈利应该被找的零钱。如果他没带够钱,那么输出的应该是负数。

    +> 输入样例1:
    +> 10.16.27 14.1.28
    +> 
    +> 输出样例1:
    +> 3.2.1
    +> 
    +> 输入样例2:
    +> 14.1.28 10.16.27
    +> 
    +> 输出样例2:
    +> -3.2.1
    +> 
    +>
    + +## 思路 + +换零钱、时间差,都是类似的思路,先换成最小单位,相减,在换回去。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1037.c),欢迎交流 +```c +#include + +int main() +{ + int Galleon, Sickle, Knut, P, A, change; + + scanf("%d.%d.%d", &Galleon, &Sickle, &Knut); + P = (Galleon * 17 + Sickle) * 29 + Knut; + scanf("%d.%d.%d", &Galleon, &Sickle, &Knut); + A = (Galleon * 17 + Sickle) * 29 + Knut; + + change = A - P; + if(change < 0) { change = -change; putchar('-'); } + printf("%d.%d.%d", change / (17 * 29), change / 29 % 17, change % 29); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1038.md b/_posts/2018-05-03-b1038.md new file mode 100644 index 0000000..1eede9a --- /dev/null +++ b/_posts/2018-05-03-b1038.md @@ -0,0 +1,65 @@ +--- +layout: post +title: "1038. 统计同成绩学生(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 本题要求读入N名学生的成绩,将获得某一给定分数的学生人数输出。

    +>

    +> 输入格式: +>

    +>

    +> 输入在第1行给出不超过105的正整数N,即学生总人数。随后1行给出N名学生的百分制整数成绩,中间以空格分隔。最后1行给出要查询的分数个数K(不超过N的正整数),随后是K个分数,中间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中按查询顺序给出得分等于指定分数的学生人数,中间以空格分隔,但行末不得有多余空格。 +>

    +> 输入样例:
    +> 10
    +> 60 75 90 55 75 99 82 90 75 50
    +> 3 75 90 88
    +> 
    +> 输出样例:
    +> 3 2 0
    +> 
    +>
    + +## 思路 + +用int[100]数组记录每一个成绩的数量即可。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1038.c),欢迎交流 +```c +#include + +int main() +{ + int N, M, score, count[101] = {0}; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d", &score); + count[score]++; + } + + scanf("%d", &M); + for(int i = 0; i < M; i++) + { + scanf("%d", &score); + printf("%d%c", count[score], i == M - 1 ? '\n' : ' '); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1039.md b/_posts/2018-05-03-b1039.md new file mode 100644 index 0000000..4f4a0c1 --- /dev/null +++ b/_posts/2018-05-03-b1039.md @@ -0,0 +1,77 @@ +--- +layout: post +title: "1039. 到底买不买(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 小红想买些珠子做一串自己喜欢的珠串。卖珠子的摊主有很多串五颜六色的珠串,但是不肯把任何一串拆散了卖。于是小红要你帮忙判断一下,某串珠子里是否包含了全部自己想要的珠子?如果是,那么告诉她有多少多余的珠子;如果不是,那么告诉她缺了多少珠子。

    +>

    为方便起见,我们用[0-9]、[a-z]、[A-Z]范围内的字符来表示颜色。例如在图1中,第3串是小红想做的珠串;那么第1串可以买,因为包含了全部她想要的珠子,还多了8颗不需要的珠子;第2串不能买,因为没有黑色珠子,并且少了一颗红色的珠子。

    +>

    图 1
    +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例。每个测试用例分别在2行中先后给出摊主的珠串和小红想做的珠串,两串都不超过1000个珠子。 +>

    +>

    +> 输出格式: +>

    +>

    +> 如果可以买,则在一行中输出“Yes”以及有多少多余的珠子;如果不可以买,则在一行中输出“No”以及缺了多少珠子。其间以1个空格分隔。 +>

    +> 输入样例1:
    +> ppRYYGrrYBR2258
    +> YrR8RrY
    +> 
    +> 输出样例1:
    +> Yes 8
    +> 
    +> 输入样例2:
    +> ppRYYGrrYB225
    +> YrR8RrY
    +> 
    +> 输出样例2:
    +> No 2
    +> 
    +>
    + +## 思路 + +很简单且直观的方法: + +还是用简单暴力的字符记录方法:使用int[128]数组记录每种字符(颜色)的数量,直接将字符的值作为索引。 + +(更新)只用一个数组来记录。第一行记录的时候增加计数,第二行记录的时候减少计数。那么正数表示这种颜色足够,负数表示这种颜色不足。 + +将正数和负数**分别累加**。如果缺少的数量累计值为0,说明足够——可以买,否则缺少的值表示缺少了多少珠子。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1039.c),欢迎交流 +```c +#include + +int main() +{ + char c; + int record[128] = {0}; /* all ASCII characters */ + while((c = getchar()) != '\n') record[(int)c]++; + while((c = getchar()) != '\n') record[(int)c]--; + + int more = 0, less = 0; + for(int i = 0; i < 128; i++) + { + if(record[i] > 0) more += record[i]; + if(record[i] < 0) less -= record[i]; + } + + if(less) printf("No %d", less); + else printf("Yes %d", more); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1040.md b/_posts/2018-05-03-b1040.md new file mode 100644 index 0000000..7f026de --- /dev/null +++ b/_posts/2018-05-03-b1040.md @@ -0,0 +1,84 @@ +--- +layout: post +title: "1040. 有几个PAT(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 字符串APPAPT中包含了两个单词“PAT”,其中第一个PAT是第2位(P),第4位(A),第6位(T);第二个PAT是第3位(P),第4位(A),第6位(T)。

    +>

    现给定字符串,问一共可以形成多少个PAT?

    +>

    +> 输入格式: +>

    +>

    输入只有一行,包含一个字符串,长度不超过105,只包含P、A、T三种字母。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出给定字符串中包含多少个PAT。由于结果可能比较大,只输出对1000000007取余数的结果。 +>

    +> 输入样例:
    +> APPAPT
    +> 
    +> 输出样例:
    +> 2
    +> 
    +>
    + +## 思路 + +这明明是一道数学题 (-__-)=@))> o<) (这个颜表情好像Hulk打Thor的那一拳哈哈~~) +![](http://upload-images.jianshu.io/upload_images/1664395-bfb0065baac103aa.gif?imageMogr2/auto-orient/strip) + +咳咳。。 + +怎么分析呢?从前向后扫描: +- 每个**A对应的PA组合数量**是A之前**P的数量**, +- 每个**T对应的PAT组合数量**是T之前所有**A对应的PA组合数量**的累加, +- **所有的PAT组合数量**是所有**T对应的PAT组合数量**的累加 + +。。。懂?。。。 + +其实看通过率应该是很多人都会的哈。这道题属于典型的90%时间用于思考,10%时间用于码代码的类型。。。。 + +**然后证明一下这个magic number——1000000007为什么是这个数(大概等于)以及什么时候应该取模:** +我们用有符号32位整型来计数,按照我代码里的变量名:P、PA、PAT。 +- P每次自增1,因此最大会达到10^5,不需取模。 +- PA每次累加P的值,我们来看一个比较极端的情况: + - "PAPA ... (共50000对PA) ... PA",这样记录PA的组合数量,就是 + PA=1 + 2 + 3 + ... + 50000=2500050000,就已经大于int了,因此计算PA应该取模。 +- PAT每次累加PA的值,因此两个值都会比较大,必须要取模。累加之后(取模之前)的数会小于2000000013,这个值小于int的最大值2^31-1=2147483647,不会溢出(模取得过大就会在没来得及取模之前就溢出了,我觉得这是关键)。 + - 那用unsigned int呢,PA是不是不需要取模了?我觉得是的。 +- 这道题是要检查答案的,用一个素数做模应该有避免巧合答案的考虑。 + +写完这个发现刘婼的博客 http://www.liuchuo.net/archives/573 也有相关的解释,并且解题思路也不一样,是累加每个A两边P和T的个数之积。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1040.c),欢迎交流 +```c +#include + +#define LIM 1000000007 + +int main() +{ + int P = 0, PA = 0, PAT = 0; + char c; + + while((c = getchar()) != '\n') + { + if(c == 'P') P++; + if(c == 'A') PA = (PA + P) % LIM; + if(c == 'T') PAT = (PAT + PA) % LIM; + } + printf("%d", PAT); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1041.md b/_posts/2018-05-03-b1041.md new file mode 100644 index 0000000..e2ad688 --- /dev/null +++ b/_posts/2018-05-03-b1041.md @@ -0,0 +1,86 @@ +--- +layout: post +title: "1041. 考试座位号(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 每个PAT考生在参加考试时都会被分配两个座位号,一个是试机座位,一个是考试座位。正常情况下,考生在入场时先得到试机座位号码,入座进入试机状态后,系统会显示该考生的考试座位号码,考试时考生需要换到考试座位就座。但有些考生迟到了,试机已经结束,他们只能拿着领到的试机座位号码求助于你,从后台查出他们的考试座位号码。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数N(<=1000),随后N行,每行给出一个考生的信息:“准考证号 试机座位号 考试座位号”。其中准考证号由14位数字组成,座位从1到N编号。输入保证每个人的准考证号都不同,并且任何时候都不会把两个人分配到同一个座位上。 +>

    +>

    +> 考生信息之后,给出一个正整数M(<=N),随后一行中给出M个待查询的试机座位号码,以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对应每个需要查询的试机座位号码,在一行中输出对应考生的准考证号和考试座位号码,中间用1个空格分隔。 +>

    +> 输入样例:
    +> 4
    +> 10120150912233 2 4
    +> 10120150912119 4 1
    +> 10120150912126 1 3
    +> 10120150912002 3 2
    +> 2
    +> 3 4
    +> 
    +> 输出样例:
    +> 10120150912002 2
    +> 10120150912119 1
    +> 
    +>
    + +## 思路 + +这道题是按试机号码查找,在存储的时候直接将试机号码当做索引存储到相应的位置就好了。 + +如果是按学号查找,估计就变成哈希表了吧。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1041.c),欢迎交流 +```c +#include +#include + +typedef struct student{ + char ID[15]; + int test_seat; + int exam_seat; +}Student, *pStudent; + +int main() +{ + int N, M, test; + Student students[1001] = {0}; /* N >= 1000 */ + pStudent sp[1001] = {0}; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + pStudent s = students + i; /* fill in the ith in the array */ + scanf("%s %d %d", s->ID, &s->test_seat, &s->exam_seat); + sp[s->test_seat] = s; /* set index same as test number */ + } + + scanf("%d", &M); + for(int i = 0; i < M; i ++) + { + scanf("%d", &test); + printf("%s %d\n", sp[test]->ID, sp[test]->exam_seat); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1042.md b/_posts/2018-05-03-b1042.md new file mode 100644 index 0000000..965dda5 --- /dev/null +++ b/_posts/2018-05-03-b1042.md @@ -0,0 +1,64 @@ +--- +layout: post +title: "1042. 字符统计(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 请编写程序,找出一段给定文字中出现最频繁的那个英文字母。

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中给出一个长度不超过1000的字符串。字符串由ASCII码表中任意可见字符及空格组成,至少包含1个英文字母,以回车结束(回车不算在内)。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出出现频率最高的那个英文字母及其出现次数,其间以空格分隔。如果有并列,则输出按字母序最小的那个字母。统计时不区分大小写,输出小写字母。

    +> 输入样例:
    +> This is a simple TEST.  There ARE numbers and other symbols 1&2&3...........
    +> 
    +> 输出样例:
    +> e 7
    +> 
    +>
    + +## 思路 + +这道题也是够简单。 + +开一个int[26]记录字母出现次数。 +找最大值的时候从后向前找,找到的就是最大值中字母序最小的。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1042.c),欢迎交流 +```c +#include +#include + +int main() +{ + char c; + int count[26] = {0}, max = 25; + + while((c = getchar()) != '\n') + if(isalpha(c)) + count[tolower(c) - 'a']++; + + /* find forward from end in case there are multiple maximums */ + for(int i = 25; i >= 0; i--) + if(count[i] >= count[max]) + max = i; + + printf("%c %d", max + 'a', count[max]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1043.md b/_posts/2018-05-03-b1043.md new file mode 100644 index 0000000..2a94ed9 --- /dev/null +++ b/_posts/2018-05-03-b1043.md @@ -0,0 +1,61 @@ +--- +layout: post +title: "1043. 输出PATest(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序,按“PATestPATest....”这样的顺序输出,并忽略其它字符。当然,六种字符的个数不一定是一样多的,若某种字符已经输出完,则余下的字符仍按PATest的顺序打印,直到所有字符都被输出。

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中给出一个长度不超过10000的、仅由英文字母构成的非空字符串。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中按题目要求输出排序后的字符串。题目保证输出非空。

    +> 输入样例:
    +> redlesPayBestPATTopTeePHPereatitAPPT
    +> 
    +> 输出样例:
    +> PATestPATestPTetPTePePee
    +> 
    +>
    + +## 思路 + +- 记录次数的时候依然使用int[128]的数组, +- 这里不用管读到的是不是“PATest”里的字符,全记下来就好了,去判断反而浪费时间, +- 输出就看个人习惯了,我先找到出现最多的次数max,循环max次,每次输出都把6个的计数减1,计数减到0或负的就不输出了。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1043.c),欢迎交流 +```c +#include + +int main() +{ + char c, *str = "PATest"; /* use as index for count[] */ + int i, flag = 1, count[128] = {0}; /* for each ASCII char */ + + /* Read and count numbers for every character */ + while((c = getchar()) != '\n') + count[(int)c]++; + + /* Print any character in "PATest" if it is still left */ + while(flag) + for(i = 0, flag = 0; i < 6; i++) + if(count[(int)str[i]]-- > 0) /* Check the number left */ + putchar(str[i]), flag = 1; + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1044.md b/_posts/2018-05-03-b1044.md new file mode 100644 index 0000000..00652df --- /dev/null +++ b/_posts/2018-05-03-b1044.md @@ -0,0 +1,115 @@ +--- +layout: post +title: "1044. 火星数字(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 火星人是以13进制计数的:

    +>
      +>
    • 地球人的0被火星人称为tret。 +>
    • 地球人数字1到12的火星文分别为:jan, feb, mar, apr, may, jun, jly, aug, sep, oct, nov, dec。 +>
    • 火星人将进位以后的12个高位数字分别称为:tam, hel, maa, huh, tou, kes, hei, elo, syy, lok, mer, jou。 +>
    +>

    例如地球人的数字“29”翻译成火星文就是“hel mar”;而火星文“elo nov”对应地球数字“115”。为了方便交流,请你编写程序实现地球和火星数字之间的互译。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数N(<100),随后N行,每行给出一个[0, 169)区间内的数字 —— 或者是地球文,或者是火星文。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对应输入的每一行,在一行中输出翻译后的另一种语言的数字。

    +> 输入样例:
    +> 4
    +> 29
    +> 5
    +> elo nov
    +> tam
    +> 
    +> 输出样例:
    +> hel mar
    +> may
    +> 115
    +> 13
    +> 
    +>
    + +## 思路 + +- 一开始以为火星数字是阿拉伯数字的风格,末尾的零也输出了。。。 +结果火星数字是英文风格。。。。 +(不是“12个高位数字”吗,又不是“整十数字”!(ノω<。)ノ))☆.。) +火星人的心思猜不透。。。 + +- 因为不知道每一行会输入什么,可以每次读一整行,看第一个字符是字母还是数字,来判断是火星数字还是地球数字 + +- 因为火星数字不知道有一个还是两个词,我强行strtok两次,如果是NULL就算0 + + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1044.c),欢迎交流 +```c +#include +#include +#include + +char *units[] = {"tret", "jan", "feb", "mar", "apr", "may", "jun", "jly", + "aug", "sep", "oct", "nov", "dec"}; +char *tens[] = {"tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", + "syy", "lok", "mer", "jou"}; + +int Mars2Earth(char *s) +{ + if(s) + { + for(int i = 0; i < 13; i++) /* units digits */ + if(strcmp(s, units[i]) == 0) + return i; + for(int i = 1; i < 13; i++) /* tens digits */ + if(strcmp(s, tens[i - 1]) == 0) + return i * 13; + } + return 0; +} + +int main() +{ + int N, m; + char line[11]; + + fgets(line, 11, stdin); + sscanf(line, "%d", &N); + for(int i = 0; i < N; i++) + { + fgets(line, 11, stdin); + if(isdigit(line[0])) /* Earth number */ + { + sscanf(line, "%d", &m); + if(m / 13 && m % 13) + printf("%s %s\n", tens[m / 13 - 1], units[m % 13]); + if(m / 13 && m % 13 == 0) + printf("%s\n", tens[m / 13 - 1]); + if(m / 13 == 0) + printf("%s\n", units[m % 13]); + } + if(isalpha(line[0])) /* Mars number */ + { + m = Mars2Earth(strtok(line, " \n")); /* higher digit */ + m += Mars2Earth(strtok(NULL, " \n")); /* lower digit */ + printf("%d\n", m); + } + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1045.md b/_posts/2018-05-03-b1045.md new file mode 100644 index 0000000..b0e952c --- /dev/null +++ b/_posts/2018-05-03-b1045.md @@ -0,0 +1,95 @@ +--- +layout: post +title: "1045. 快速排序(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 +> 给定划分后的N个互不相同的正整数的排列,请问有多少个元素可能是划分前选取的主元?

    +>

    例如给定N = 5, 排列是1、3、2、4、5。则:

    +>

    +>

  • 1的左边没有元素,右边的元素都比它大,所以它可能是主元;
    +>
  • 尽管3的左边元素都比它小,但是它右边的2它小,所以它不能是主元;
    +>
  • 尽管2的右边元素都比它大,但其左边的3比它大,所以它不能是主元;
    +>
  • 类似原因,4和5都可能是主元。
  • +>

    因此,有3个元素可能是主元。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第1行中给出一个正整数N(<= 105); +> 第2行是空格分隔的N个不同的正整数,每个数不超过109。 +>

    +>

    +> 输出格式: +>

    +>

    在第1行中输出有可能是主元的元素个数;在第2行中按递增顺序输出这些元素,其间以1个空格分隔,行末不得有多余空格。 +>

    +> 输入样例:
    +> 5
    +> 1 3 2 4 5
    +> 
    +> 输出样例:
    +> 3
    +> 1 4 5
    +> 
    +>
    + +## 思路 + +我第一次AC的思路是主元的位置在全部排序之后是不变的,并且需要比前面的数字都要大。这样需要对数组进行排序,时间复杂度O(N log(N))。 + +然后看了网上别人的一些代码(很多,就不给出链接了),发现不用对数组进行排序(所以这个代码不是我独立思考出来的,郑重说明): + +- 只需要记录每个数左边的最大值和右边的最小值(都包含这个数本身), + - 从左向右和从右向左各遍历一次即可完成,需要另两个数组, +- 二者和该数都相等,这个数就可能是主元。 + +时间复杂度O(N)。这种思路对主元的性质把握的很准确,我这道题很多次都没有过,就是因为思路一直很乱。 + +当然还有一点,如果没有可能的主元,也要在第二行输出个换行,否则会有*格式错误*,被认为没有第二行。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1045.c),欢迎交流 +```c +#include + +int main() +{ + int N, count = 0; + int array[100000], lmax[100000], rmin[100000]; + + scanf("%d", &N); + for(int i = 0; i < N; i++) scanf("%d", array + i); + + /* Find the largest on one's left and the smallest on the right */ + for(int i = 0, max = i; i < N; i++) + lmax[i] = array[i] >= array[max] ? array[max = i] : array[max]; + for(int i = N - 1, min = i; i >= 0; i--) + rmin[i] = array[i] <= array[min] ? array[min = i] : array[min]; + + /* A element is the largest on its left and the smallest on its right, + * it is probably a pivot */ + for(int i = 0; i < N; i++) + { + if(array[i] == lmax[i] && array[i] == rmin[i]) + count++; + else + array[i] = 0; + } + + printf("%d\n", count); + for(int i = 0; i < N && count; i++) if(array[i]) + printf("%d%c", array[i], --count ? ' ' : '\0'); + printf("\n"); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1046.md b/_posts/2018-05-03-b1046.md new file mode 100644 index 0000000..1bca4f6 --- /dev/null +++ b/_posts/2018-05-03-b1046.md @@ -0,0 +1,67 @@ +--- +layout: post +title: "1046. 划拳(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 划拳是古老中国酒文化的一个有趣的组成部分。酒桌上两人划拳的方法为:每人口中喊出一个数字,同时用手比划出一个数字。如果谁比划出的数字正好等于两人喊出的数字之和,谁就赢了,输家罚一杯酒。两人同赢或两人同输则继续下一轮,直到唯一的赢家出现。 +>

    +>

    下面给出甲、乙两人的划拳记录,请你统计他们最后分别喝了多少杯酒。

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行先给出一个正整数N(<=100),随后N行,每行给出一轮划拳的记录,格式为:

    +>

    甲喊 甲划 乙喊 乙划

    +>

    其中“喊”是喊出的数字,“划”是划出的数字,均为不超过100的正整数(两只手一起划)。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中先后输出甲、乙两人喝酒的杯数,其间以一个空格分隔。

    +> 输入样例:
    +> 5
    +> 8 10 9 12
    +> 5 10 5 10
    +> 3 8 5 12
    +> 12 18 1 13
    +> 4 16 12 15
    +> 
    +> 输出样例:
    +> 1 2
    +> 
    +>
    + +## 思路 + +15分的题都是送分题,好像没有可解释的。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1046.c),欢迎交流 +```c +#include + +int main() +{ + int N, A1, A2, B1, B2; + int A = 0, B = 0; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d %d %d %d", &A1, &A2, &B1, &B2); + if(A2 == A1 + B1 && B2 != A1 + B1) B++; + if(A2 != A1 + B1 && B2 == A1 + B1) A++; + } + printf("%d %d", A, B); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1047.md b/_posts/2018-05-03-b1047.md new file mode 100644 index 0000000..e5b39d1 --- /dev/null +++ b/_posts/2018-05-03-b1047.md @@ -0,0 +1,70 @@ +--- +layout: post +title: "1047. 编程团体赛(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 编程团体赛的规则为:每个参赛队由若干队员组成;所有队员独立比赛;参赛队的成绩为所有队员的成绩和;成绩最高的队获胜。 +>

    +>

    +> 现给定所有队员的比赛成绩,请你编写程序找出冠军队。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数N(<=10000),即所有参赛队员总数。随后N行,每行给出一位队员的成绩,格式为:“队伍编号-队员编号 成绩”,其中“队伍编号”为1到1000的正整数,“队员编号”为1到10的正整数,“成绩”为0到100的整数。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出冠军队的编号和总成绩,其间以一个空格分隔。注意:题目保证冠军队是唯一的。 +>

    +> 输入样例:
    +> 6
    +> 3-10 99
    +> 11-5 87
    +> 102-1 0
    +> 102-3 100
    +> 11-9 89
    +> 3-2 61
    +> 
    +> 输出样例:
    +> 11 176
    +> 
    +>
    + +## 思路 + +将队伍编号作为数组索引即可方便地累加同队队员的成绩。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1047.c),欢迎交流 +```c +#include + +int main() +{ + int N, team, member, score, highest = 0, teams[1000] = {0}; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d-%d %d", &team, &member, &score); + teams[team - 1] += score; + } + for(int i = 0; i < 1000; i++) + if(teams[i] > teams[highest]) + highest = i; + printf("%d %d", highest + 1, teams[highest]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1048.md b/_posts/2018-05-03-b1048.md new file mode 100644 index 0000000..bb78ed5 --- /dev/null +++ b/_posts/2018-05-03-b1048.md @@ -0,0 +1,68 @@ +--- +layout: post +title: "1048. 数字加密(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 本题要求实现一种数字加密方法。首先固定一个加密用正整数A,对任一正整数B,将其每1位数字与A的对应位置上的数字进行以下运算:对奇数位,对应位的数字相加后对13取余——这里用J代表10、Q代表11、K代表12;对偶数位,用B的数字减去A的数字,若结果为负数,则再加10。这里令个位为第1位。

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中依次给出A和B,均为不超过100位的正整数,其间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出加密后的结果。 +>

    +> 输入样例:
    +> 1234567 368782971
    +> 
    +> 输出样例:
    +> 3695Q8118
    +> 
    +>
    + +## 思路 + +要注意位数不足的话在前面补0。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1048.c),欢迎交流 +```c +#include +#include + +int main() +{ + char A[101], B[101]; + char encrypt[] = "0123456789JQK"; + + scanf("%s %s", A, B); + int lenA = strlen(A); + int lenB = strlen(B); + int maxlen = lenA > lenB ? lenA : lenB; + int a, b; + + for(int i = 0; i < maxlen; i++) + { + a = lenA + i - maxlen < 0 ? 0 : A[lenA + i - maxlen] - '0'; + b = lenB + i - maxlen < 0 ? 0 : B[lenB + i - maxlen] - '0'; + + if((maxlen - i) % 2) + putchar(encrypt[(a + b) % 13]); + else + putchar('0' + (b - a < 0 ? b - a + 10 : b - a)); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1049.md b/_posts/2018-05-03-b1049.md new file mode 100644 index 0000000..8a227f2 --- /dev/null +++ b/_posts/2018-05-03-b1049.md @@ -0,0 +1,83 @@ +--- +layout: post +title: "1049. 数列的片段和(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段。例如,给定数列{0.1, 0.2, 0.3, 0.4},我们有(0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3) (0.2, 0.3, 0.4) (0.3) (0.3, 0.4) (0.4) 这10个片段。

    +>

    给定正整数数列,求出全部片段包含的所有的数之和。如本例中10个片段总和是0.1 + 0.3 + 0.6 + 1.0 + 0.2 + 0.5 + 0.9 + 0.3 + 0.7 + 0.4 = 5.0。

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个不超过105的正整数N,表示数列中数的个数,第二行给出N个不超过1.0的正数,是数列中的数,其间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出该序列所有片段包含的数之和,精确到小数点后2位。 +>

    +> 输入样例:
    +> 4
    +> 0.1 0.2 0.3 0.4 
    +> 
    +> 输出样例:
    +> 5.00
    +> 
    +>
    + +## 思路 + +又是一道数学题。 +解题的代码很少,不过在下面的分析中还加入了浮点型数据精度的分析,虽然不是必要的。 + +- 先计算出数列中每一个元素在所有片段和中被包含的次数: + + 每一个包含a[i]的片段需要在a[i]左侧(包含a[i])和a[i]右侧(也包含a[i])各选取一个端点。我们使用0开始的计数。左侧端点选取可能有i+1种,右侧端点选取可能有N-i种。 + + 因此包含a[i]的片段和一共有(i+1)(N-i)种,做一个加权求和即可求出片段和:![](http://latex.codecogs.com/svg.latex?\sum_{i=0}^{N-1}(i+1)(N-i)a_i) +- **严谨起见的分析:** 最大可能的片段和(我们要用的变量类型): + + 令N=100000,任意i都有a[i]=1.0,此时的片段和为 + + ![](http://latex.codecogs.com/svg.latex?\sum_{i=1}^{N}i(N-i+1)\\=-\sum_{i=1}^{N}i^2+(N+1)\sum_{i=1}^{N}i\\=-\frac{N(N+1)(2N+1)}{6}+\frac{N(N+1)^2}{2}\\=\frac{1}{6}N(N+1)(N+2)) + + 这个数约等于1.67e14,题目要求精度达到小数点后2位,即相当于**相对误差最多为6e-17**。然后我们看一下不同浮点型[(Wiki)](https://en.wikipedia.org/wiki/Floating-point_arithmetic)的精度: + + - 单精度浮点的误差:尾数部分有23位,精度为1.2e-7; + - 双精度浮点的误差:尾数部分有52位,精度为2.2e-16; + - 扩展精度的浮点型的误差:尾数部分有63位,精度为1.1e-19; + + 可以看出单精度浮点是绝对不能用的,虽然双精度浮点的误差略大于上面分析的结果,但是我觉得PAT不会(事实上也没有)挖这个坑,所以用`double`类型的会过(我没有试过`float`)。如果要写更加严谨的代码,应使用`long double`。 + +- 还有一个问题,看了[这篇博客](http://blog.csdn.net/luoluozlb/article/details/51532281)才发现,同样是数据范围的问题。(i+1)(N-i)最大值约为N^2/4也就是2.5e9,32位有符号整型`int`的最大值是2^31-1,约为2e9,先乘这两个整数可能会发生溢出。那篇博客说在求和时先算这两个整数的乘积会有测试点过不去,就是测试点在测试N很大的情况。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1049.c),欢迎交流 +```c +#include + +int main() +{ + int N; + double ai, sum = 0; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%lf", &ai); + /* ai is put at the beginning to avoid overflow */ + sum += ai * (i + 1) * (N - i); + } + printf("%.2lf", sum); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1050.md b/_posts/2018-05-03-b1050.md new file mode 100644 index 0000000..6e3d12c --- /dev/null +++ b/_posts/2018-05-03-b1050.md @@ -0,0 +1,107 @@ +--- +layout: post +title: "1050. 螺旋矩阵(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 本题要求将给定的N个正整数按非递增的顺序,填入“螺旋矩阵”。所谓“螺旋矩阵”,是指从左上角第1个格子开始,按顺时针螺旋方向填充。要求矩阵的规模为m行n列,满足条件:m*n等于N;m>=n;且m-n取所有可能值中的最小值。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第1行中给出一个正整数N,第2行给出N个待填充的正整数。所有数字不超过104,相邻数字以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 输出螺旋矩阵。每行n个数字,共m行。相邻数字以1个空格分隔,行末不得有多余空格。 +>

    +> 输入样例:
    +> 12
    +> 37 76 20 98 76 42 53 95 60 81 58 93
    +> 
    +> 输出样例:
    +> 98 95 93
    +> 42 37 81
    +> 53 20 76
    +> 58 60 76
    +> 
    +>
    + +## 思路 + +- 确定m和n:m是大于sqrt(N)且能整除N的最小整数,故只需m从1递增,直到满足m * m >= N && N % m == 0,即可找到m,然后n = N / m。 + +- **填充旋转矩阵:** 这个估计很难直接找出通项公式,所以一般的方法都是乖乖地一圈一圈地赋值。那么关键就是 **如何控制坐标实现在矩阵里“螺旋地”遍历** ,这里总结一下我看到的方法中代表性的几个: + - (递减法)我用的方法,每次(螺旋地向内)填充一个边,长度依次递减:n,m-1,n-1,m-2,n-2,m-3,……,直到长度到0; + - (矩形法)一圈一圈的填,每次通过矩形四边的坐标确定边界,如 +http://www.liuchuo.net/archives/2070 ; + - (碰壁法)每次到达矩阵外部或者已经填过的,便变化方向,如 +http://www.cnblogs.com/zhien-aa/p/5671115.html , +http://blog.csdn.net/luoluozlb/article/details/51567610 。并且后者使用两个int[4]数组代表4个方向的做法很棒,能使代码减少重复。 + + 为什么我感觉找到的三篇都是女生的(・∀・(・∀・(・∀・*) + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1050.c),欢迎交流 +```c +#include +#include + +int cmp(const void *a, const void *b) +{ + return *(int*)b - *(int*)a; +} + +int main() +{ + int N, m, n; + int array[10000] = {0}, matrix[10000] = {0}; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + scanf("%d", array + i); + + qsort(array, N, sizeof(int), cmp); + + /* determine m and n */ + for(m = 1; !(m * m >= N && N % m == 0); m++) ; + n = N / m; + + int x = -1, y = 0, index = 0; + int horizontal = n, virtical = m; + + while(horizontal > 0 && virtical > 0) + { + for(int i = 0; i < horizontal && virtical > 0; i++) /* toward right */ + matrix[y * n + ++x] = array[index++]; + virtical--; + + for(int i = 0; i < virtical && horizontal > 0; i++) /* toward bottom */ + matrix[++y * n + x] = array[index++]; + horizontal--; + + for(int i = 0; i < horizontal && virtical > 0; i++) /* toward left */ + matrix[y * n + --x] = array[index++]; + virtical--; + + for(int i = 0; i < virtical && horizontal > 0; i++) /* toward top */ + matrix[--y * n + x] = array[index++]; + horizontal--; + } + + for(int i = 0; i < m; i++) + for(int j = 0; j < n; j++) + printf("%d%c", matrix[i * n + j], j == n - 1 ? '\n' : ' '); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1051.md b/_posts/2018-05-03-b1051.md new file mode 100644 index 0000000..ffb4558 --- /dev/null +++ b/_posts/2018-05-03-b1051.md @@ -0,0 +1,66 @@ +--- +layout: post +title: "1051. 复数乘法 (15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 复数可以写成(A + Bi)的常规形式,其中A是实部,B是虚部,i是虚数单位,满足i2 = -1;也可以写成极坐标下的指数形式(R*e(Pi)),其中R是复数模,P是辐角,i是虚数单位,其等价于三角形式 R(cos(P) + isin(P))。

    +>

    现给定两个复数的R和P,要求输出两数乘积的常规形式。

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中依次给出两个复数的R1, P1, R2, P2,数字间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中按照“A+Bi”的格式输出两数乘积的常规形式,实部和虚部均保留2位小数。注意:如果B是负数,则应该写成“A-|B|i”的形式。 +>

    +> 输入样例:
    +> 2.3 3.5 5.2 0.4
    +> 
    +> 输出样例:
    +> -8.68-8.23i
    +> 
    +>
    + +## 思路 + +复数乘法的计算可以先化直角坐标,在相乘;也可以先相乘,在化为直角坐标。然而显然后者更简单。 + +这道题的“坑”在于结果的输出。 +C语言的格式化输出虽然能正常四舍五入,但是有一点貌似和一般的自然写法不同:很接近0的负数四舍五入之后不输出0.00,而是-0.00,这点就要特殊照顾了。 + +浮点精度:用`float`是不能通过的,一开始还不明白,后来想想,可能题目对输入的范围没有提示,有测试点输入了很大的模,单精度的精度只有1.2e-7,R1和R2大一些就可能不够了。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1051.c),欢迎交流 +```c +#include +#include + +int main() +{ + double R1, P1, R2, P2; /* float type won't pass, not sure why */ + double A, B; + + scanf("%lf %lf %lf %lf", &R1, &P1, &R2, &P2); + A = R1 * R2 * cos(P1 + P2); /* doesn't matter how you calculate */ + B = R1 * R2 * sin(P1 + P2); + + if(A < 0 && A > -0.005) A = 0; /* Rounding */ + if(B < 0 && B > -0.005) B = 0; + + printf("%.2lf%+.2lfi", A, B); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1052.md b/_posts/2018-05-03-b1052.md new file mode 100644 index 0000000..8bb3b34 --- /dev/null +++ b/_posts/2018-05-03-b1052.md @@ -0,0 +1,103 @@ +--- +layout: post +title: "1052. 卖个萌 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 萌萌哒表情符号通常由“手”、“眼”、“口”三个主要部分组成。简单起见,我们假设一个表情符号是按下列格式输出的:

    +>
    +> [左手]([左眼][口][右眼])[右手]
    +> 
    +>

    +> 现给出可选用的符号集合,请你按用户的要求输出表情。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入首先在前三行顺序对应给出手、眼、口的可选符号集。每个符号括在一对方括号[]内。题目保证每个集合都至少有一个符号,并不超过10个符号;每个符号包含1到4个非空字符。 +>

    +>

    +> 之后一行给出一个正整数K,为用户请求的个数。随后K行,每行给出一个用户的符号选择,顺序为左手、左眼、口、右眼、右手——这里只给出符号在相应集合中的序号(从1开始),数字间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对每个用户请求,在一行中输出生成的表情。若用户选择的序号不存在,则输出“Are you kidding me? @\/@”。 +>

    +> 输入样例:
    +> [╮][╭][o][~\][/~]  [<][>]
    +>  [╯][╰][^][-][=][>][<][@][⊙]
    +> [Д][▽][_][ε][^]  ...
    +> 4
    +> 1 1 2 2 2
    +> 6 8 1 5 5
    +> 3 3 4 3 3
    +> 2 10 3 9 3
    +> 
    +> 输出样例:
    +> ╮(╯▽╰)╭
    +> <(@Д=)/~
    +> o(^ε^)o
    +> Are you kidding me? @\/@
    +> 
    +>
    + +## 思路 + +1 **变长编码([Wiki: Variable-width encoding](https://en.wikipedia.org/wiki/Variable-width_encoding)):** + +先了解一下这些颜表情符号是怎么能用8bit大小的字符输出的:如果用一字节一字节地读取,ASCII字符之外的字符会分解为多个字节,并且能够和单字节字符区分开,如拆分成多个的字节都大于127,这样就知道那些字符应该组合成多字节字符了。 +- 如"I♥NY"(I love NewYork)的编码为49(I) **E2 99 A5**(♥) 4E(N) 59(Y)。 + +经验证,题目中说的 +> 每个符号包含1到4个非空字符。 + +就是指每个符号长度不超过4个字节。因此存储需要**char[3][10][5]**数组即可。 + +2 **表情符号读取:** + +在之前的[1024. 科学计数法 (20)(C语言实现)](http://www.jianshu.com/p/ba0282bb87a0)中,我使用了格式化字符串```"%[^...]"```,这种字符串的作用类似于`"%s"`,但是后者会读取字符串到空白字符(空格、换行、制表符等等),前者使scanf读到^后面的字符,相当于用户可以自定义scanf的行为。所以这是这道题的一把利剑啊! + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1052.c),欢迎交流 +```c +#include + +/* About special characters, see: + * https://en.wikipedia.org/wiki/Variable-width_encoding */ + +int main() +{ + int N, m[5]; + char c, symbols[3][10][5] = {0}; + + for(int symbol = 0; symbol < 3; symbol++) + for(int index = 0; (c = getchar()) != '\n'; ) + if(c == '[') scanf("%[^]]", symbols[symbol][index++]); + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + for(int i = 0; i < 5; i++) scanf("%d", m + i); + if(m[0] > 0 && m[0] <= 10 && *symbols[0][--m[0]] + && m[1] > 0 && m[1] <= 10 && *symbols[1][--m[1]] + && m[2] > 0 && m[2] <= 10 && *symbols[2][--m[2]] + && m[3] > 0 && m[3] <= 10 && *symbols[1][--m[3]] + && m[4] > 0 && m[4] <= 10 && *symbols[0][--m[4]]) + printf("%s(%s%s%s)%s\n", symbols[0][m[0]], symbols[1][m[1]], + symbols[2][m[2]], symbols[1][m[3]], symbols[0][m[4]]); + else + puts("Are you kidding me? @\\/@"); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1053.md b/_posts/2018-05-03-b1053.md new file mode 100644 index 0000000..bed6f72 --- /dev/null +++ b/_posts/2018-05-03-b1053.md @@ -0,0 +1,88 @@ +--- +layout: post +title: "1053. 住房空置率 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 在不打扰居民的前提下,统计住房空置率的一种方法是根据每户用电量的连续变化规律进行判断。判断方法如下: +>

    +>
      +>
    • 在观察期内,若存在超过一半的日子用电量低于某给定的阈值e,则该住房为“可能空置”; +>
    • 若观察期超过某给定阈值D天,且满足上一个条件,则该住房为“空置”。 +>
    +>

    现给定某居民区的住户用电量数据,请你统计“可能空置”的比率和“空置”比率,即以上两种状态的住房占居民区住房总套数的百分比。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出正整数N(<=1000),为居民区住房总套数;正实数e,即低电量阈值;正整数D,即观察期阈值。随后N行,每行按以下格式给出一套住房的用电量数据: +>

    +>

    +> K E1 E2 ... EK +>

    +>

    +> 其中K为观察的天数,Ei为第i天的用电量。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出“可能空置”的比率和“空置”比率的百分比值,其间以一个空格分隔,保留小数点后1位。 +>

    +> 输入样例:
    +> 5 0.5 10
    +> 6 0.3 0.4 0.5 0.2 0.8 0.6
    +> 10 0.0 0.1 0.2 0.3 0.0 0.8 0.6 0.7 0.0 0.5
    +> 5 0.4 0.3 0.5 0.1 0.7
    +> 11 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
    +> 11 2 2 2 1 1 0.1 1 0.1 0.1 0.1 0.1
    +> 
    +> 输出样例:
    +> 40.0% 20.0%
    +> 
    +>

    +> (样例解释:第2、3户为“可能空置”,第4户为“空置”,其他户不是空置。) +>

    +>
    + +## 思路 + + +就是输入有些多而已,没有什么难度。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1053.c),欢迎交流 +```c +#include + +int main() +{ + int N, D, K; + int empty = 0, pempty = 0, lower; + float e, E; + + scanf("%d %f %d", &N, &e, &D); + for(int i = 0; i < N; i++) + { + lower = 0; + scanf("%d", &K); + for(int j = 0; j < K; j++) + { + scanf("%f", &E); + if(E < e) lower++; + } + if(lower > K / 2 && K > D) empty++; + else if(lower > K / 2) pempty++; + } + printf("%.1f%% %.1f%%", 100.0 * pempty / N, 100.0 * empty / N); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1054.md b/_posts/2018-05-03-b1054.md new file mode 100644 index 0000000..ca3c4a8 --- /dev/null +++ b/_posts/2018-05-03-b1054.md @@ -0,0 +1,112 @@ +--- +layout: post +title: "1054. 求平均值 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 本题的基本要求非常简单:给定N个实数,计算它们的平均值。但复杂的是有些输入数据可能是非法的。一个“合法”的输入是[-1000,1000]区间内的实数,并且最多精确到小数点后2位。当你计算平均值的时候,不能把那些非法的数据算在内。

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出正整数N(<=100)。随后一行给出N个实数,数字间以一个空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对每个非法输入,在一行中输出“ERROR: X is not a legal number”,其中X是输入。最后在一行中输出结果:“The average of K numbers is Y”,其中K是合法输入的个数,Y是它们的平均值,精确到小数点后2位。如果平均值无法计算,则用“Undefined”替换Y。如果K为1,则输出“The average of 1 number is Y”。 +>

    +> 输入样例1:
    +> 7
    +> 5 -3.2 aaa 9999 2.3.4 7.123 2.35
    +> 
    +> 输出样例1:
    +> ERROR: aaa is not a legal number
    +> ERROR: 9999 is not a legal number
    +> ERROR: 2.3.4 is not a legal number
    +> ERROR: 7.123 is not a legal number
    +> The average of 3 numbers is 1.38
    +> 
    +> 输入样例2:
    +> 2
    +> aaa -9999
    +> 
    +> 输出样例2:
    +> ERROR: aaa is not a legal number
    +> ERROR: -9999 is not a legal number
    +> The average of 0 numbers is Undefined
    +> 
    +>
    + +## 思路 + +**先说说输入:** 我们不清楚题目会给出多长的非法输入,因此为了**防止溢出**,我们应该选择一个安全的方式应对这种情况。 +我的方法是先确定合法数字的最长长度:8个字符,那么我们每次就先只读8个字符。如果输入流(`stdin`)中的下一个字符不是空白字符,那么这个数就绝对非法;如果是空白字符的话,再继续讨论接下来的问题。 +**(这个问题几乎网上所有AC的方法都是用的一个大数组来读取,应该是PAT对这一点没有严格检查(要是有上千字符的输入就不行了)。我只是出于健壮性考虑,照顾到了这一点)** + +(通过率0.19的题我一次AC了,先让我激动一会儿 `\(≧▽≦)/` ) + +**代码实现** + +- 读取8个字符,使用了`scanf("%8s", str)`,就是读到空白字符或者8个字符。 +- 判断是否是浮点,使用了函数`strtod(const char *, char **)`,这个函数的第二个参数是一个指针的地址,用来使该指针指向未用于转换的第一个字符,我们可以根据这个指针是否为`NULL`来判断这个字符串是否为浮点。 +(e.g. 如第一个参数传入字符串2.3.4,第二个参数这个指针会指向第二个小数点) +- 精度,找到小数点(有的话)的位置,看它距字符串结尾有几个字符就好了,不能大于3个。 + +P.S. 在网上看到了结合使用sscanf和sprintf的方法,相当的巧妙。忘记了在哪里,读者可自行查找,或尝试自己去实现。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1054.c),欢迎交流 +```c +#include +#include +#include +#include + +int main() +{ + int count = 0, N; + double f, sum = 0; + /* Maxium scenario: -1000.00. So just need to read 8 chars(+ '\0' = 9) */ + char s[9], *pEnd, *pDot, c; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%8s", s); /* Just read up to 8 chars */ + + c = ungetc(getchar(), stdin); /* Read next char and push back */ + f = strtod(s, &pEnd); /* pEnd -> converted floating number */ + pDot = strchr(s, '.'); /* pDot -> (first) decimal point */ + + if(!isspace(c) /* string too long */ + || *pEnd /* not floating number */ + || (f > 1000 || f < -1000) /* out of range */ + || (pDot && pDot - s < strlen(s) - 3)) /* precision too high */ + { + printf("ERROR: %s", s); + /* this can avoid overflow (we don't know how long input is) */ + while(!isspace(c = getchar())) putchar(c); + printf(" is not a legal number\n"); + } + else /* legel number */ + { + count++; + sum += f; + } + } + + if(count == 0) printf("The average of 0 numbers is Undefined\n"); + if(count == 1) printf("The average of 1 number is %.2lf", sum); + if(count >= 2) printf("The average of %d numbers is %.2lf", count, sum / count); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1055.md b/_posts/2018-05-03-b1055.md new file mode 100644 index 0000000..511284d --- /dev/null +++ b/_posts/2018-05-03-b1055.md @@ -0,0 +1,118 @@ +--- +layout: post +title: "1055. 集体照 (25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 拍集体照时队形很重要,这里对给定的N个人K排的队形设计排队规则如下: +>

    +>
      +>
    • 每排人数为N/K(向下取整),多出来的人全部站在最后一排; +>
    • 后排所有人的个子都不比前排任何人矮; +>
    • 每排中最高者站中间(中间位置为m/2+1,其中m为该排人数,除法向下取整); +>
    • 每排其他人以中间人为轴,按身高非增序,先右后左交替入队站在中间人的两侧(例如5人身高为190、188、186、175、170,则队形为175、188、190、186、170。这里假设你面对拍照者,所以你的左边是中间人的右边); +>
    • 若多人身高相同,则按名字的字典序升序排列。这里保证无重名。 +>
    +>

    现给定一组拍照人,请编写程序输出他们的队形。

    +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例。每个测试用例第1行给出两个正整数N(<=10000,总人数)和K(<=10,总排数)。随后N行,每行给出一个人的名字(不包含空格、长度不超过8个英文字母)和身高([30, 300]区间内的整数)。 +>

    +>

    +> 输出格式: +>

    +>

    +> 输出拍照的队形。即K排人名,其间以空格分隔,行末不得有多余空格。注意:假设你面对拍照者,后排的人输出在上方,前排输出在下方。 +>

    +> 输入样例:
    +> 10 3
    +> Tom 188
    +> Mike 170
    +> Eva 168
    +> Tim 160
    +> Joe 190
    +> Ann 168
    +> Bob 175
    +> Nick 186
    +> Amy 160
    +> John 159
    +> 
    +> 输出样例:
    +> Bob Tom Joe Nick
    +> Ann Mike Eva
    +> Tim Amy John
    +> 
    +>
    + +## 思路 + +- 利用`qsort`排序,排序按照身高降序,身高相同按照名字字典序升序; +- 第一行人数为N - N / K * (K - 1),剩下每一行人数为N / K; +- 每一行从左开始先倒序输出奇数位的人名,再顺序输出偶数位的人名。这里按照已排好的顺序从0计数。e.g. 输出7个人名就是5 3 1 0 2 4 6, + - 在处理空格的时候,奇数部分是不用担心的,只需在偶数部分判断最后一位; + - 最大的奇数是 一行总人数 / 2 * 2 - 1。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1055.c),欢迎交流 +```c +#include +#include + +typedef struct student{ + char name[9]; + int height; +}student, *Student; + +int cmp(const void *a, const void *b) +{ + Student s1 = *(Student*)a; + Student s2 = *(Student*)b; + if(s1->height != s2->height) + return s2->height - s1->height; + return strcmp(s1->name, s2->name); +} + +/** + * Print one row + * Return the address of the beginning of next line + **/ +Student *printrow(Student *s, int num) +{ + for(int i = num / 2 * 2 - 1; i > 0; i -= 2) /* the left half */ + printf("%s ", s[i]->name); + for(int i = 0; i < num; i += 2) /* the right half */ + printf("%s%c", s[i]->name, i + 2 < num ? ' ' : '\n'); + return s + num; +} + +int main() +{ + /* read and sort data */ + int N, K; + student students[10000] = {0}; + Student sp[10000] = {0}, *p = sp; + + scanf("%d %d", &N, &K); + for(int i = 0; i < N; i++) + { + sp[i] = students + i; + scanf("%s %d", sp[i]->name, &sp[i]->height); + } + + qsort(sp, N, sizeof(Student), cmp); + + /* print */ + p = printrow(p, N - N / K * (K - 1)); /* the last row */ + while(p < sp + N) + p = printrow(p, N / K); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1056.md b/_posts/2018-05-03-b1056.md new file mode 100644 index 0000000..1f4edda --- /dev/null +++ b/_posts/2018-05-03-b1056.md @@ -0,0 +1,57 @@ +--- +layout: post +title: "1056. 组合数的和(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 给定N个非0的个位数字,用其中任意2个数字都可以组合成1个2位的数字。要求所有可能组合出来的2位数字的和。例如给定2、5、8,则可以组合出:25、28、52、58、82、85,它们的和为330。

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中先给出N(1<N<10),随后是N个不同的非0个位数字。数字间以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 输出所有可能组合出来的2位数字的和。 +>

    +> 输入样例:
    +> 3 2 8 5
    +> 
    +> 输出样例:
    +> 330
    +> 
    +>
    + +## 思路 + +相当简单,做个小小的数学题,结果是![](http://latex.codecogs.com/svg.latex?11(N-1)\sum_{i=1}^Na_i) + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1056.c),欢迎交流 +```c +#include + +int main() +{ + int N, a, sum = 0; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d", &a); + sum += a; + } + printf("%d", sum * (N - 1) * 11); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1057.md b/_posts/2018-05-03-b1057.md new file mode 100644 index 0000000..640dcb8 --- /dev/null +++ b/_posts/2018-05-03-b1057.md @@ -0,0 +1,60 @@ +--- +layout: post +title: "1057. 数零壹(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    给定一串长度不超过105的字符串,本题要求你将其中所有英文字母的序号(字母a-z对应序号1-26,不分大小写)相加,得到整数N,然后再分析一下N的二进制表示中有多少0、多少1。例如给定字符串“PAT (Basic)”,其字母序号之和为:16+1+20+2+1+19+9+3=71,而71的二进制是1000111,即有3个0、4个1。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中给出长度不超过105、以回车结束的字符串。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中先后输出0的个数和1的个数,其间以空格分隔。 +>

    +> 输入样例:
    +> PAT (Basic)
    +> 
    +> 输出样例:
    +> 3 4
    +> 
    +>
    + +## 思路 + +逐字符读取,边读边累加。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1057.c),欢迎交流 +```c +#include +#include + +int main() +{ + char c; + int sum = 0, count[2] = {0}; + + while((c = getchar()) != '\n') + if(isalpha(c)) + sum += tolower(c) - 'a' + 1; + + for(; sum; sum >>= 1) + count[sum & 1]++; + + printf("%d %d", count[0], count[1]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1058.md b/_posts/2018-05-03-b1058.md new file mode 100644 index 0000000..8131686 --- /dev/null +++ b/_posts/2018-05-03-b1058.md @@ -0,0 +1,126 @@ +--- +layout: post +title: "1058. 选择题(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    批改多选题是比较麻烦的事情,本题就请你写个程序帮助老师批改多选题,并且指出哪道题错的人最多。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出两个正整数N(<=1000)和M(<=100),分别是学生人数和多选题的个数。随后M行,每行顺次给出一道题的满分值(不超过5的正整数)、选项个数(不少于2且不超过5的正整数)、正确选项个数(不超过选项个数的正整数)、所有正确选项。注意每题的选项从小写英文字母a开始顺次排列。各项间以1个空格分隔。最后N行,每行给出一个学生的答题情况,其每题答案格式为“(选中的选项个数 选项1 ……)”,按题目顺序给出。注意:题目保证学生的答题情况是合法的,即不存在选中的选项数超过实际选项数的情况。 +>

    +>

    +> 输出格式: +>

    +>

    +> 按照输入的顺序给出每个学生的得分,每个分数占一行。注意判题时只有选择全部正确才能得到该题的分数。最后一行输出错得最多的题目的错误次数和编号(题目按照输入的顺序从1开始编号)。如果有并列,则按编号递增顺序输出。数字间用空格分隔,行首尾不得有多余空格。如果所有题目都没有人错,则在最后一行输出“Too simple”。 +>

    +> 输入样例:
    +> 3 4 
    +> 3 4 2 a c
    +> 2 5 1 b
    +> 5 3 2 b c
    +> 1 5 4 a b d e
    +> (2 a c) (2 b d) (2 a c) (3 a b e)
    +> (2 a c) (1 b) (2 a b) (4 a b d e)
    +> (2 b d) (1 e) (2 b c) (4 a b c d)
    +> 
    +> 输出样例:
    +> 3
    +> 6
    +> 5
    +> 2 2 3 4
    +> 
    +>
    + +## 思路 + +这道题稍微复杂一些,但是拆分成一个个要点,每一点又是很简单的。 + +- 使用结构数组存储每一道题的分数、选项数、正确选项数、正确选项和学生答错数量。 +当然如何存储这些量每个人不尽相同,下面是我的方法: + - 在一个整型变量中按位存储一道题的正确选项,如答案是a b d e,那么这个量就是二进制00011011(从低向高存储), + - 这样直接比较整型的大小就知道答案是否一致,并且相当于选项数、正确选项数、正确选项三项信息综合在了一起。 +- 读取 + - 读取学生答案的时候,要注意如何处理括号, + - 发现C语言读取空格分隔的字母还挺麻烦。我是用的`while((c = getchar()) != ' ');`读到非空白字符。 + +写出来代码好多,是乙级里代码最多的几题了╮(╯▽╰)╭(难道是我的问题?) + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1058.c),欢迎交流 +```c +#include + +typedef struct prob{ + int score; + int answer; /* bitwise storage for at most 5 options */ + int wrong; +} Prob; + +/* read 'count option1 ...' format */ +int readanswer() +{ + char c; + int count, answer = 0; + scanf("%d", &count); + for(int k = 0; k < count; k++) + { + while((c = getchar()) == ' ') ; + answer |= 1 << (c - 'a'); + } + return answer; +} + +int main() +{ + int N, M, max = 0, useless; + Prob probs[100]; + + /* read the answers for each problem */ + scanf("%d %d", &N, &M); + for(int i = 0; i < M; i++) + { + scanf("%d %d", &probs[i].score, &useless); + probs[i].wrong = 0; + probs[i].answer = readanswer(); + } + + /* read every student's answer */ + for(int i = 0; i < N; i++) + { + int score = 0; + for(int j = 0; j < M; j++) + { + /* read answer for one problem */ + while(getchar() != '('); + if(readanswer() == probs[j].answer) /* If it is right */ + score += probs[j].score; + else if(max < ++probs[j].wrong) /* If most students got it wrong */ + max = probs[j].wrong; + while(getchar() != ')'); + } + printf("%d\n", score); + } + + if(max == 0) + printf("Too simple"); + else + { + printf("%d", max); + for(int i = 0; i < M; i++) + if(probs[i].wrong == max) + printf(" %d", i + 1); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1059.md b/_posts/2018-05-03-b1059.md new file mode 100644 index 0000000..dc20213 --- /dev/null +++ b/_posts/2018-05-03-b1059.md @@ -0,0 +1,108 @@ +--- +layout: post +title: "1059. C语言竞赛(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> C语言竞赛是浙江大学计算机学院主持的一个欢乐的竞赛。既然竞赛主旨是为了好玩,颁奖规则也就制定得很滑稽: +>

    +>

    +> 0. 冠军将赢得一份“神秘大奖”(比如很巨大的一本学生研究论文集……)。
    +> 1. 排名为素数的学生将赢得最好的奖品 —— 小黄人玩偶!
    +> 2. 其他人将得到巧克力。

    +>

    +> 给定比赛的最终排名以及一系列参赛者的ID,你要给出这些参赛者应该获得的奖品。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数N(<=10000),是参赛者人数。随后N行给出最终排名,每行按排名顺序给出一位参赛者的ID(4位数字组成)。接下来给出一个正整数K以及K个需要查询的ID。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对每个要查询的ID,在一行中输出“ID: 奖品”,其中奖品或者是“Mystery Award”(神秘大奖)、或者是“Minion”(小黄人)、或者是“Chocolate”(巧克力)。如果所查ID根本不在排名里,打印“Are you kidding?”(耍我呢?)。如果该ID已经查过了(即奖品已经领过了),打印“ID: Checked”(不能多吃多占)。 +>

    +> 输入样例:
    +> 6
    +> 1111
    +> 6666
    +> 8888
    +> 1234
    +> 5555
    +> 0001
    +> 6
    +> 8888
    +> 0001
    +> 1111
    +> 2222
    +> 8888
    +> 2222
    +> 
    +> 输出样例:
    +> 8888: Minion
    +> 0001: Chocolate
    +> 1111: Mystery Award
    +> 2222: Are you kidding?
    +> 8888: Checked
    +> 2222: Are you kidding?
    +> 
    +>
    + +## 思路 + +也几乎没有难度,记录5种状态:没有排名、三种奖励和已经查过。 + +还有不要把素数看成奇数(不要问我怎么知道的) + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1059.c),欢迎交流 +```c +#include + +int isPrime(int n) +{ + if(n == 1) return 0; + for(int i = 2; i * i <= n; i++) + if(n % i == 0) + return 0; + return 1; +} + +int main() +{ + int award[10000] = {0}, N, K, ID; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d", &ID); + if(i == 0) award[ID] = 1; + else if(isPrime(i + 1)) award[ID] = 2; + else award[ID] = 3; + } + scanf("%d", &K); + for(int i = 0; i < K; i++) + { + scanf("%d", &ID); + switch(award[ID]) + { + case 0: printf("%04d: Are you kidding?\n", ID); break; + case 1: printf("%04d: Mystery Award\n", ID); award[ID] = 4; break; + case 2: printf("%04d: Minion\n", ID); award[ID] = 4; break; + case 3: printf("%04d: Chocolate\n", ID); award[ID] = 4; break; + case 4: printf("%04d: Checked\n", ID); break; + } + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1060.md b/_posts/2018-05-03-b1060.md new file mode 100644 index 0000000..b6b271c --- /dev/null +++ b/_posts/2018-05-03-b1060.md @@ -0,0 +1,70 @@ +--- +layout: post +title: "1060. 爱丁顿数(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 英国天文学家爱丁顿很喜欢骑车。据说他为了炫耀自己的骑车功力,还定义了一个“爱丁顿数”E,即满足有E天骑车超过E英里的最大整数E。据说爱丁顿自己的E等于87。 +>

    +>

    +> 现给定某人N天的骑车距离,请你算出对应的爱丁顿数E(<=N)。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数N(<=105),即连续骑车的天数;第二行给出N个非负整数,代表每天的骑车距离。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中给出N天的爱丁顿数。

    +> 输入样例:
    +> 10
    +> 6 7 6 9 3 10 8 2 7 8
    +> 
    +> 输出样例:
    +> 6
    +> 
    +>
    + +## 思路 + +降序排列,天数大于序数的最大序数。 + +已经有很多道25分题是考验数学能力的了,比如[1040. 有几个PAT(25)(C语言实现)](http://www.jianshu.com/p/65291de1906b),不过这道题数学上也没什么难度 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1060.c),欢迎交流 +```c +#include +#include + +int cmp(const void *a, const void *b) +{ + return *(int*)b - *(int*)a; +} + +int main() +{ + int N, E, miles[100000]; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + scanf("%d", miles + i); + + qsort(miles, N, sizeof(int), cmp); + + for(E = 0; E < N && miles[E] > E + 1; E++) ; + printf("%d", E); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1061.md b/_posts/2018-05-03-b1061.md new file mode 100644 index 0000000..447d153 --- /dev/null +++ b/_posts/2018-05-03-b1061.md @@ -0,0 +1,76 @@ +--- +layout: post +title: "1061. 判断题(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    判断题的评判很简单,本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出两个不超过100的正整数N和M,分别是学生人数和判断题数量。第二行给出M个不超过5的正整数,是每道题的满分值。第三行给出每道题对应的正确答案,0代表“非”,1代表“是”。随后N行,每行给出一个学生的解答。数字间均以空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 按照输入的顺序输出每个学生的得分,每个分数占一行。 +>

    +> 输入样例:
    +> 3 6
    +> 2 1 3 3 4 5
    +> 0 0 1 0 1 1
    +> 0 1 1 0 0 1
    +> 1 0 1 0 1 0
    +> 1 1 0 0 1 1
    +> 
    +> 输出样例:
    +> 13
    +> 11
    +> 12
    +> 
    +>
    + +## 思路 + +确实很简单 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1061.c),欢迎交流 +```c +#include + +int main() +{ + int N, M; + int fullmark[100], answer[100]; + int score, choice; + + scanf("%d %d", &N, &M); + for(int i = 0; i < M; i++) + scanf("%d", fullmark + i); + for(int i = 0; i < M; i++) + scanf("%d", answer + i); + + for(int i = 0; i < N; i++) + { + score = 0; + for(int j = 0; j < M; j++) + { + scanf("%d", &choice); + if(choice == answer[j]) + score += fullmark[j]; + } + printf("%d\n", score); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1062.md b/_posts/2018-05-03-b1062.md new file mode 100644 index 0000000..bb077c9 --- /dev/null +++ b/_posts/2018-05-03-b1062.md @@ -0,0 +1,73 @@ +--- +layout: post +title: "1062. 最简分数(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    一个分数一般写成两个整数相除的形式:N/M,其中M不为0。最简分数是指分子和分母没有公约数的分数表示形式。 +>

    +>

    现给定两个不相等的正分数 N1/M1 和 N2/M2,要求你按从小到大的顺序列出它们之间分母为K的最简分数。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中按N/M的格式给出两个正分数,随后是一个正整数分母K,其间以空格分隔。题目保证给出的所有整数都不超过1000。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中按N/M的格式列出两个给定分数之间分母为K的所有最简分数,按从小到大的顺序,其间以1个空格分隔。行首尾不得有多余空格。题目保证至少有1个输出。 +>

    +> 输入样例:
    +> 7/18 13/20 12
    +> 
    +> 输出样例:
    +> 5/12 7/12
    +> 
    +>
    + +## 思路 + +一个坑就是给出的两个分数并没有指定谁大谁小,看了别人的解法才反应过来的。怪不得通过率只有0.18。 + +还有一个注意点就是开始结束的点: +- 不能等于给出的两个分数,因为要求“两个数之间的”最简分数 +- 开始点:需要 L > K \* N1 / M1 >= L - 1,由于C语言整型的“地板除”运算,一定有 K \* N1 / M1 = L - 1,那么初始点就是 L = K \* N1 / M1 + 1。这样不需要用循环来确定。 +- 结束点:要避免“地板除”带来的问题,使用 N2 \* K > M2 \* L 作为判断标准。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1062.c),欢迎交流 +```c +#include + +int gcd(int a, int b) +{ + for(int r; (r = a % b); a = b, b = r) ; + return b; +} + +int main() +{ + int N1, N2, M1, M2, K, L, count = 0; + scanf("%d/%d %d/%d %d", &N1, &M1, &N2, &M2, &K); + + if(N1 * M2 > N2 * M1) + { + L = N1, N1 = N2, N2 = L; + L = M1, M1 = M2, M2 = L; + } + + for(L = N1 * K / M1 + 1; N2 * K > M2 * L; L++) + if(gcd(L, K) == 1) + printf("%s%d/%d", count++ ? " " : "", L, K); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1063.md b/_posts/2018-05-03-b1063.md new file mode 100644 index 0000000..f4b856d --- /dev/null +++ b/_posts/2018-05-03-b1063.md @@ -0,0 +1,66 @@ +--- +layout: post +title: "1063. 计算谱半径(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 在数学中,矩阵的“谱半径”是指其特征值的模集合的上确界。换言之,对于给定的n个复数空间的特征值{a1+b1i, ..., an+bni},它们的模为实部与虚部的平方和的开方,而“谱半径”就是最大模。 +>

    +>

    现在给定一些复数空间的特征值,请你计算并输出这些特征值的谱半径。

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出正整数N(<= 10000)是输入的特征值的个数。随后N行,每行给出1个特征值的实部和虚部,其间以空格分隔。注意:题目保证实部和虚部均为绝对值不超过1000的整数。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出谱半径,四舍五入保留小数点后2位。 +>

    +> 输入样例:
    +> 5
    +> 0 1
    +> 2 0
    +> -1 0
    +> 3 3
    +> 0 -3
    +> 
    +> 输出样例:
    +> 4.24
    +> 
    +>
    + +## 思路 + +真的好简单,题目输入范围也很小,也没有什么输入格式的特例。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1063.c),欢迎交流 +```c +#include +#include + +int main() +{ + int N, R, I, norm = 0; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d %d", &R, &I); + if(R * R + I * I > norm) + norm = R * R + I * I; + } + printf("%.2lf", sqrt(norm)); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1064.md b/_posts/2018-05-03-b1064.md new file mode 100644 index 0000000..d5c1c2c --- /dev/null +++ b/_posts/2018-05-03-b1064.md @@ -0,0 +1,66 @@ +--- +layout: post +title: "1064. 朋友数(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 如果两个整数各位数字的和是一样的,则被称为是“朋友数”,而那个公共的和就是它们的“朋友证号”。例如123和51就是朋友数,因为1+2+3 = 5+1 = 6,而6就是它们的朋友证号。给定一些整数,要求你统计一下它们中有多少个不同的朋友证号。注意:我们默认一个整数自己是自己的朋友。

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出正整数N。随后一行给出N个正整数,数字间以空格分隔。题目保证所有数字小于104。 +>

    +>

    +> 输出格式: +>

    +>

    +> 首先第一行输出给定数字中不同的朋友证号的个数;随后一行按递增顺序输出这些朋友证号,数字间隔一个空格,且行末不得有多余空格。 +>

    +> 输入样例:
    +> 8
    +> 123 899 51 998 27 33 36 12
    +> 
    +> 输出样例:
    +> 4
    +> 3 6 9 26
    +> 
    +>
    + +## 思路 + +最大的各位数字和是36,开一个int[36]的数组即可。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1064.c),欢迎交流 +```c +#include + +int main() +{ + int N, m, sum, friendID[37] = {0}, count = 0; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d", &m); + for(sum = 0; m; m /= 10) + sum += m % 10; + if(!friendID[sum]) + friendID[sum] = 1, count++; + } + + printf("%d\n", count); + for(int i = 1; i < 37; i++) + if(friendID[i]) + printf("%d%c", i, --count ? ' ' : '\0'); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1065.md b/_posts/2018-05-03-b1065.md new file mode 100644 index 0000000..9694b26 --- /dev/null +++ b/_posts/2018-05-03-b1065.md @@ -0,0 +1,105 @@ +--- +layout: post +title: "1065. 单身狗(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> “单身狗”是中文对于单身人士的一种爱称。本题请你从上万人的大型派对中找出落单的客人,以便给予特殊关爱。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数N(<=50000),是已知夫妻/伴侣的对数;随后N行,每行给出一对夫妻/伴侣——为方便起见,每人对应一个ID号,为5位数字(从00000到99999),ID间以空格分隔;之后给出一个正整数M(<=10000),为参加派对的总人数;随后一行给出这M位客人的ID,以空格分隔。题目保证无人重婚或脚踩两条船。 +>

    +>

    +> 输出格式: +>

    +>

    +> 首先第一行输出落单客人的总人数;随后第二行按ID递增顺序列出落单的客人。ID间用1个空格分隔,行的首尾不得有多余空格。 +>

    +> 输入样例:
    +> 3
    +> 11111 22222
    +> 33333 44444
    +> 55555 66666
    +> 7
    +> 55555 44444 10000 88888 22222 11111 23333
    +> 
    +> 输出样例:
    +> 5
    +> 10000 23333 44444 55555 88888
    +> 
    +>
    + +## 思路 + +很有......单身狗歧视的一道题! ! ! (为什么分值最多的题要提及这么沉重的话题 (;′⌒`)) + +设计了一个方法, 只用一个int[100000]数组来解决问题: +1. 一对夫妇ID保存在数组中两个元素中, 形成双向的映射, 这个基本所有人都是这么做的 +2. 逐一读取来客ID + - 如果ID有对应值, 说明有配偶, 数组相应位置标记"已签到"(使用宏定义定义为一个负值) + - 如果ID没有对应值, 说明是单身狗, 数组相应位置标记"落单"(另一个宏定义) +3. 遍历数组, 不管值为"已签到"和"落单"的. 如果一个ID对应有非负值(即合法ID值,说明他/她是有配偶的), 进一步如果这个值对应值是"已签到",说明ID没有来, 而他/她的配偶来了(对照上面操作不难理解). ID这个人算是"落单",标记上 +4. 再遍历一次, 值为"落单"的输出即可 + +看了一下网上其他人的做法, 很多都用了高级语言的功能, 如Python的dict, C++的set和map(只用C的表示完全不知道用C++的在做什么╮(╯▽╰)╭)。当然还有时间复杂度O(N*M)的. +我的方法没有使用任何的高级数据结构/算法, 写出的代码也挺简洁的, 只需要遍历两边数组, 运行时间不到10ms, 还有点小骄傲呢^_^ + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1065.c),欢迎交流 +```c +#include + +#define BLANK -1 +#define SIGNED -2 +#define SINGLE -3 + +int main() +{ + int couple[100000], count = 0, N, M, ID1, ID2; + for(int i = 0; i < 100000; i++) + couple[i] = BLANK; + + /* Read 'couple-list', every pair of 'index' and 'value' are a couple. */ + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d %d", &ID1, &ID2); + couple[ID1] = ID2; + couple[ID2] = ID1; + } + + scanf("%d", &M); + for(int i = 0; i < M; i++) /* Read guest list. */ + { + scanf("%d", &ID1); + if(couple[ID1] >= 0) /* If one has a mate */ + couple[ID1] = SIGNED; /* set SIGNED */ + else /* Else: not in the 'couple-list' */ + couple[ID1] = SINGLE, count++; /* set SINGLE */ + } + + /* If couple[ID] is >= 0 (but not signed) but couple[couple[ID]] == SIGNED + * (signed in), this means 'ID' didn't come but his/her mate did. + * So his/her mate is alone in the party, set to SINGLE. */ + for(int i = 0; i < 100000; i++) + if(couple[i] >= 0 && couple[couple[i]] == SIGNED) + couple[couple[i]] = SINGLE, count++; + + /* Those whose value is SINGLE is either a bachelor or came alone */ + printf("%d\n", count); + for(int i = 0; i < 100000; i++) + if(couple[i] == SINGLE) + printf("%05d%c", i, --count ? ' ' : '\0'); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1066.md b/_posts/2018-05-03-b1066.md new file mode 100644 index 0000000..93dd8e1 --- /dev/null +++ b/_posts/2018-05-03-b1066.md @@ -0,0 +1,63 @@ +--- +layout: post +title: "1066. 图像过滤(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    图像过滤是把图像中不重要的像素都染成背景色,使得重要部分被凸显出来。现给定一幅黑白图像,要求你将灰度值位于某指定区间内的所有像素颜色都用一种指定的颜色替换。

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出一幅图像的分辨率,即两个正整数M和N(0 < M, N <= 500),另外是待过滤的灰度值区间端点A和B(0 <= A < B <= 255)、以及指定的替换灰度值。随后M行,每行给出N个像素点的灰度值,其间以空格分隔。所有灰度值都在[0, 255]区间内。 +>

    +>

    +> 输出格式: +>

    +>

    +> 输出按要求过滤后的图像。即输出M行,每行N个像素灰度值,每个灰度值占3位(例如黑色要显示为000),其间以一个空格分隔。行首尾不得有多余空格。 +>

    +> 输入样例:
    +> 3 5 100 150 0
    +> 3 189 254 101 119
    +> 150 233 151 99 100
    +> 88 123 149 0 255
    +> 
    +> 输出样例:
    +> 003 189 254 000 000
    +> 000 233 151 099 000
    +> 088 000 000 000 255
    +> 
    +>
    + +## 思路 + +很简单的题目,甚至连数组都不用开。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1066.c),欢迎交流 +```c +#include + +int main() +{ + int N, M, A, B, C, D; + + scanf("%d %d %d %d %d", &M, &N, &A, &B, &C); + + for(int i = 0; i < M; i++) + for(int j = 0; j < N; j++) + { + scanf("%d", &D); + if(A <= D && D <= B) D = C; + printf("%03d%c", D, j == N - 1 ? '\n' : ' '); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1067.md b/_posts/2018-05-03-b1067.md new file mode 100644 index 0000000..e6ec10f --- /dev/null +++ b/_posts/2018-05-03-b1067.md @@ -0,0 +1,102 @@ +--- +layout: post +title: "1067. 试密码(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    当你试图登录某个系统却忘了密码时,系统一般只会允许你尝试有限多次,当超出允许次数时,账号就会被锁死。本题就请你实现这个小功能。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出一个密码(长度不超过20的、不包含空格、Tab、回车的非空字符串)和一个正整数N(<= 10),分别是正确的密码和系统允许尝试的次数。随后每行给出一个以回车结束的非空字符串,是用户尝试输入的密码。输入保证至少有一次尝试。当读到一行只有单个#字符时,输入结束,并且这一行不是用户的输入。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对用户的每个输入,如果是正确的密码且尝试次数不超过N,则在一行中输出“Welcome in”,并结束程序;如果是错误的,则在一行中按格式输出“Wrong password: 用户输入的错误密码”;当错误尝试达到N次时,再输出一行“Account locked”,并结束程序。 +>

    +> 输入样例1:
    +> Correct%pw 3
    +> correct%pw
    +> Correct@PW
    +> whatisthepassword!
    +> Correct%pw
    +> #
    +> 
    +> 输出样例1:
    +> Wrong password: correct%pw
    +> Wrong password: Correct@PW
    +> Wrong password: whatisthepassword!
    +> Account locked
    +> 
    +> 输入样例2:
    +> cool@gplt 3
    +> coolman@gplt
    +> coollady@gplt
    +> cool@gplt
    +> try again
    +> #
    +> 
    +> 输出样例2:
    +> Wrong password: coolman@gplt
    +> Wrong password: coollady@gplt
    +> Welcome in
    +> 
    +>
    + +## 思路 + +很考察思路严谨的一道题,真的要考虑周全输入密码时的情况。 + +正确密码是没有任何空白字符的,而用户输入可以在一行内任意输入,因此接受用户输入应该以换行符为判断标准。 + +C不方便的地方就是读字符串必须提前设定长度,而这道题的用户输入并没有任何限制,因此在程序中应该考虑防溢出的机制(比如边界检查): +- 我每次只读20个字符或者读到换行,达到这样的目的,使用了`scanf("%20[^\n]")`,```[^...]```能够读取到^后边的字符,而不是`%s`读到空白字符。 +- 如果用户还有输入,再**逐一**读取字符,一直输出到换行,这样保证了程序的健壮性。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1067.c),欢迎交流 +```c +#include +#include +#include + +int main() +{ + int N; + char c, correct[21], user[21]; + + scanf("%s %d", correct, &N); + while(getchar() != '\n'); + while(N--) + { + scanf("%20[^\n]", user); /* avoid overflow */ + c = ungetc(getchar(), stdin); + if(!strcmp(user, "#") && c == '\n') + { + break; + } + else if(!strcmp(correct, user) && c == '\n') + { + puts("Welcome in"); + break; + } + else + { + printf("Wrong password: %s", user); + while(putchar(getchar()) != '\n') ; + if(!N) puts("Account locked"); + } + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1068.md b/_posts/2018-05-03-b1068.md new file mode 100644 index 0000000..b1797fa --- /dev/null +++ b/_posts/2018-05-03-b1068.md @@ -0,0 +1,165 @@ +--- +layout: post +title: "1068. 万绿丛中一点红(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    对于计算机而言,颜色不过是像素点对应的一个24位的数值。现给定一幅分辨率为MxN的画,要求你找出万绿丛中的一点红,即有独一无二颜色的那个像素点,并且该点的颜色与其周围8个相邻像素的颜色差充分大。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出三个正整数,分别是M和N(<= 1000),即图像的分辨率;以及TOL,是所求像素点与相邻点的颜色差阈值,色差超过TOL的点才被考虑。随后N行,每行给出M个像素的颜色值,范围在[0, 224)内。所有同行数字间用空格或TAB分开。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中按照“(x, y): color”的格式输出所求像素点的位置以及颜色值,其中位置x和y分别是该像素在图像矩阵中的列、行编号(从1开始编号)。如果这样的点不唯一,则输出“Not Unique”;如果这样的点不存在,则输出“Not Exist”。 +>

    +> 输入样例1:
    +> 8 6 200
    +> 0 	 0 	  0 	   0	    0 	     0 	      0        0
    +> 65280 	 65280    65280    16711479 65280    65280    65280    65280
    +> 16711479 65280    65280    65280    16711680 65280    65280    65280
    +> 65280 	 65280    65280    65280    65280    65280    165280   165280
    +> 65280 	 65280 	  16777015 65280    65280    165280   65480    165280
    +> 16777215 16777215 16777215 16777215 16777215 16777215 16777215 16777215
    +> 
    +> 输出样例1:
    +> (5, 3): 16711680
    +> 
    +> 输入样例2:
    +> 4 5 2
    +> 0 0 0 0
    +> 0 0 3 0
    +> 0 0 0 0
    +> 0 5 0 0
    +> 0 0 0 0
    +> 
    +> 输出样例2:
    +> Not Unique
    +> 
    +> 输入样例3:
    +> 3 3 5
    +> 1 2 3
    +> 3 4 5
    +> 5 6 7
    +> 
    +> 输出样例3:
    +> Not Exist
    +> 
    +>
    + +## 思路 + +在这道题上我好像想多了,当然也有可能陈越姥姥想少了(怎么可能,不可能的)。 + +首先就是,**“颜色差”** 是什么?网上其他人的做法99%(可能100%)都是直接把两个颜色的值相减,结果的整数就是颜色差了。 +而我参考了[Wiki: Color difference](https://en.wikipedia.org/wiki/Color_difference), +将(R, G, B)三维颜色空间的“距离”作为颜色差,这也是很合理的。比如65280,也就是#00FF00,是绿色, +而65279,#00FEFF,是青色,两者在整数大小上相差1,但是颜色有很大区别。幸好(或许也是不幸)在测试用例没有使用上面这样的例子, +我的测试结果和大家都是一样的。 + +然后就是大家都遇到的难(keng)点: + +- 第一个难(keng)点: + +> 要求你找出万绿丛中的一点红,即有独一无二颜色的那个像素点 + + 解读:需要这个颜色在整幅图中只出现一次。 + +- 第二个难(keng)点:虽然题目说 + +> 该点的颜色与其周围**8个**相邻像素的颜色差充分大。 + + 但是题目还是设置了处于边界的满足“条件”的点,所以说这又是一个题目叙述不清楚的案例(好多受害者)。 + + 应对这个问题目前看到的代码中有两个解决方案,一是加入边界特殊情况的检测,二是在题目数据的周围加一圈0。 + 第一个方案没有问题。第二个方案我觉得是有问题的,如图片的**左上角**有这样的数据: +``` +#000001 #0000FF ...... +#0000FF #0000FF ...... +...... ...... ...... +``` + 假设颜色#000001在图片只出现了这一次,这样这个点应该符合要求。但是周围加上0就变成了 +``` +#000000 #000000 #000000 ...... +#000000 #000001 #0000FF ...... +#000000 #0000FF #0000FF ...... +...... ...... ...... ...... +``` + 不管颜色差是怎么算的,这个中间的`#000001`都是不能被检测出来的。但是又很幸运地,我看使用这个方法的代码也都通过了。 + 我能说什么呢 :) + +P.S. + +**关于代码优化**:这个代码运行时间好长(160+ms),应该是判断颜色唯一太耗时了,相当于O(N^2\*M^2), +或许改为用另一个排序好的列表查找会好很多(预计时间复杂度为M\*N\*log(M\*N))。 + +**更新:上面的判断竟然是错的**,我在本地测试了上述方案,使用最多的数据1000*1000,排序加查找竟然比原来的方案耗时多一个数量级 +~~~o(>﹏<)o。之前的方案从文件读取用了0.2秒,但是查找只用了0.04秒(其中iUnique函数只有0.02秒!)。 +新的方案qsort用了0.2秒,查找(使用了bsearch)用了0.3秒!这不科学 (ㄒoㄒ) (难道是被stdlib坑了?) +(证明瓶颈在数据读取上,那运行时间长的锅我就不背了) + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1068.c),欢迎交流 +```c +#include + +#define SQR(X) ((X)*(X)) +#define R(COLOR) ((COLOR & 0XFF0000) >> 16) +#define G(COLOR) ((COLOR & 0X00FF00) >> 8) +#define B(COLOR) (COLOR & 0X0000FF) +#define D(C1, C2) (SQR(R(C1) - R(C2)) + SQR(G(C1) - G(C2)) + SQR(B(C1) - B(C2))) + +int iUnique(int array[][1000], int x, int y, int x0, int y0) +{ + for(int i = 0; i < x; i++) + for(int j = 0; j < y; j++) + if(array[i][j] == array[x0][y0] && i != x0 && j != y0) + return 0; + return 1; +} + +int main() +{ + int M, N, TOL; + scanf("%d %d %d", &M, &N, &TOL); + + int fig[1000][1000]; + for(int i = 0; i < N; i++) + for(int j = 0; j < M; j++) + scanf("%d", &fig[i][j]); + + int count = 0, M0, N0; + for(int i = 0; i < N; i ++) + for (int j = 0; j < M; j++) + if((i == 0 ? 1 : D(fig[i][j], fig[i - 1][j - 1]) > SQR(TOL)) + && (i == 0 ? 1 : D(fig[i][j], fig[i - 1][j ]) > SQR(TOL)) + && (i == 0 ? 1 : D(fig[i][j], fig[i - 1][j + 1]) > SQR(TOL)) + && (j == 0 ? 1 : D(fig[i][j], fig[i ][j - 1]) > SQR(TOL)) + && (j == M - 1 ? 1 : D(fig[i][j], fig[i ][j + 1]) > SQR(TOL)) + && (i == N - 1 ? 1 : D(fig[i][j], fig[i + 1][j - 1]) > SQR(TOL)) + && (i == N - 1 ? 1 : D(fig[i][j], fig[i + 1][j ]) > SQR(TOL)) + && (i == N - 1 ? 1 : D(fig[i][j], fig[i + 1][j + 1]) > SQR(TOL)) + && iUnique(fig, N, M, i, j)) + { + count++; + N0 = i; + M0 = j; + } + + if(count == 0) printf("Not Exist"); + if(count == 1) printf("(%d, %d): %d", M0 + 1, N0 + 1, fig[N0][M0]); + if(count >= 2) printf("Not Unique"); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1069.md b/_posts/2018-05-03-b1069.md new file mode 100644 index 0000000..3c4b533 --- /dev/null +++ b/_posts/2018-05-03-b1069.md @@ -0,0 +1,101 @@ +--- +layout: post +title: "1069. 微博转发抽奖(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 小明PAT考了满分,高兴之余决定发起微博转发抽奖活动,从转发的网友中按顺序每隔N个人就发出一个红包。请你编写程序帮助他确定中奖名单。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出三个正整数M(<= 1000)、N和S,分别是转发的总量、小明决定的中奖间隔、以及第一位中奖者的序号(编号从1开始)。随后M行,顺序给出转发微博的网友的昵称(不超过20个字符、不包含空格回车的非空字符串)。 +>

    +>

    +> 注意:可能有人转发多次,但不能中奖多次。所以如果处于当前中奖位置的网友已经中过奖,则跳过他顺次取下一位。 +>

    +>

    +> 输出格式: +>

    +>

    +> 按照输入的顺序输出中奖名单,每个昵称占一行。如果没有人中奖,则输出“Keep going...”。 +>

    +> 输入样例1:
    +> 9 3 2
    +> Imgonnawin!
    +> PickMe
    +> PickMeMeMeee
    +> LookHere
    +> Imgonnawin!
    +> TryAgainAgain
    +> TryAgainAgain
    +> Imgonnawin!
    +> TryAgainAgain
    +> 
    +> 输出样例1:
    +> PickMe
    +> Imgonnawin!
    +> TryAgainAgain
    +> 
    +> 输入样例2:
    +> 2 3 5
    +> Imgonnawin!
    +> PickMe
    +> 
    +> 输出样例2:
    +> Keep going...
    +> 
    +>
    + +## 思路 + +好聪明的小明啊。 + +我用了count记录以中奖的人数,r记录因重复而跳过的人数,所以得到奖品的序号应该是 S + count \* N + r + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1069.c),欢迎交流 +```c +#include +#include + +int main() +{ + char s[1000][21] = {{0}}, line[21]; + int M, N, S, m, count = 0, r = 0; + + scanf("%d %d %d", &M, &N, &S); + + for(m = 0, S--; m < M; m++) + { + scanf("%s", line); + if(m == S + count * N + r) + { + int flag = 1; + for(int i = 0; i < count && flag; i++) + if(!strcmp(s[i], line)) + { + flag = 0; + r++; + break; + } + if(flag) + strcpy(s[count++], line); + } + } + + for(int i = 0; i < count; i++) + printf("%s\n", s[i]); + if(!count) + printf("Keep going..."); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1070.md b/_posts/2018-05-03-b1070.md new file mode 100644 index 0000000..9565666 --- /dev/null +++ b/_posts/2018-05-03-b1070.md @@ -0,0 +1,76 @@ +--- +layout: post +title: "1070. 结绳(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    给定一段一段的绳子,你需要把它们串成一条绳。每次串连的时候,是把两段绳子对折,再如下图所示套接在一起。这样得到的绳子又被当成是另一段绳子,可以再次对折去跟另一段绳子串连。每次串连后,原来两段绳子的长度就会减半。

    +>
    +>

    +> 给定N段绳子的长度,你需要找出它们能串成的绳子的最大长度。 +>

    +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例。每个测试用例第1行给出正整数N (2 <= N <= 104);第2行给出N个正整数,即原始绳段的长度,数字间以空格分隔。所有整数都不超过104。

    +>

    +> 输出格式: +>

    +>

    在一行中输出能够串成的绳子的最大长度。结果向下取整,即取为不超过最大长度的最近整数。

    +> 输入样例:
    +> 8
    +> 10 15 12 3 4 13 1 15
    +> 
    +> 输出样例:
    +> 14
    +> 
    +>
    + +## 思路 + +一开始以为是huffman编码,其实不用那么麻烦。每次找到最小的两个结成新绳子(求平均)即可。 + +由于要从小到大来找绳子,当然排序是首先想到的方法。但是绳子的长度不超过10^4,所以既然最多的情况都要用int[10000]的数组,不如巧妙地用它来记录数量而不是长度,这样就不需要排序,时间复杂度为O(N)。 + +最短的绳子不需要和之前最短的绳子求平均,但如果设初值为最短绳子的长度,就没有这个特殊处理了,第一个for循环就是做这个。 + +看出来了,25分的题要不是考验数学能力的,就是间接考察数据结构和算法的。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1070.c),欢迎交流 +```c +#include + +int main() +{ + int l[10001] = {0}, N, i; + double length = 0; + + scanf("%d", &N); + for(int j = 0; j < N; j++) + { + scanf("%d", &i); + l[i]++; /* record counts */ + } + + for(i = 0; i < 10001; i++) /* find the shortest, special case */ + if(l[i]) + { + length = i; + break; + } + + for(; i < 10001; i++) /* make new ropes */ + while(l[i]--) + length = (length + i) / 2; + + printf("%d", (int)length); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1071.md b/_posts/2018-05-03-b1071.md new file mode 100644 index 0000000..7b210ef --- /dev/null +++ b/_posts/2018-05-03-b1071.md @@ -0,0 +1,110 @@ +--- +layout: post +title: "1071. 小赌怡情(15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    常言道“小赌怡情”。这是一个很简单的小游戏:首先由计算机给出第一个整数;然后玩家下注赌第二个整数将会比第一个数大还是小;玩家下注t个筹码后,计算机给出第二个数。若玩家猜对了,则系统奖励玩家t个筹码;否则扣除玩家t个筹码。 +>

    +>

    +> 注意:玩家下注的筹码数不能超过自己帐户上拥有的筹码数。当玩家输光了全部筹码后,游戏就结束。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出2个正整数T和K(<=100),分别是系统在初始状态下赠送给玩家的筹码数、以及需要处理的游戏次数。随后K行,每行对应一次游戏,顺序给出4个数字: +>

    +>

    +> n1 b t n2 +>

    +>

    +> 其中n1和n2是计算机先后给出的两个[0, 9]内的整数,保证两个数字不相等。b为0表示玩家赌“小”,为1表示玩家赌“大”。t表示玩家下注的筹码数,保证在整型范围内。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对每一次游戏,根据下列情况对应输出(其中t是玩家下注量,x是玩家当前持有的筹码量): +>

    +>
      +>
    • 玩家赢,输出
      Win t!  Total = x.
      +>
    • 玩家输,输出
      Lose t.  Total = x.
      +>
    • 玩家下注超过持有的筹码量,输出
      Not enough tokens.  Total = x.
      +>
    • 玩家输光后,输出
      Game Over.
      并结束程序。 +>
    +> 输入样例1:
    +> 100 4
    +> 8 0 100 2
    +> 3 1 50 1
    +> 5 1 200 6
    +> 7 0 200 8
    +> 
    +> 输出样例1:
    +> Win 100!  Total = 200.
    +> Lose 50.  Total = 150.
    +> Not enough tokens.  Total = 150.
    +> Not enough tokens.  Total = 150.
    +> 
    +> 输入样例2:
    +> 100 4
    +> 8 0 100 2
    +> 3 1 200 1
    +> 5 1 200 6
    +> 7 0 200 8
    +> 
    +> 输出样例2:
    +> Win 100!  Total = 200.
    +> Lose 200.  Total = 0.
    +> Game Over.
    +> 
    +>
    + +## 思路 + +很简单的题目,几个if-else语句嵌套,判断的顺序弄清楚就好了。 +我在做这道题的时候(2017/9/21),系统只接受同一行的两句之间有两个空格的输出,而这和样例是不一样的(只有一个空格)。我觉得以后会对此有修改的。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1071.c),欢迎交流 +```c +#include + +int main() +{ + int T, K, n1, n2, b, t; + scanf("%d %d", &T, &K); + for(int i = 0; i < K; i ++) + { + scanf("%d %d %d %d", &n1, &b, &t, &n2); + if(t <= T) + { + if((b == 0 && n2 < n1) || (b == 1 && n2 > n1)) + { + T += t; + printf("Win %d! Total = %d.\n", t, T); + } + else + { + T -= t; + printf("Lose %d. Total = %d.\n", t, T); + if(T <= 0) + { + printf("Game Over.\n"); + break; + } + } + } + else + { + printf("Not enough tokens. Total = %d.\n", T); + } + } + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1072.md b/_posts/2018-05-03-b1072.md new file mode 100644 index 0000000..fa52fc6 --- /dev/null +++ b/_posts/2018-05-03-b1072.md @@ -0,0 +1,97 @@ +--- +layout: post +title: "1072. 开学寄语(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    下图是上海某校的新学期开学寄语:天将降大任于斯人也,必先删其微博,卸其QQ,封其电脑,夺其手机,收其ipad,断其wifi,使其百无聊赖,然后,净面、理发、整衣,然后思过、读书、锻炼、明智、开悟、精进。而后必成大器也! +>

    +>
    +>

    +> 本题要求你写个程序帮助这所学校的老师检查所有学生的物品,以助其成大器。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出两个正整数N(<= 1000)和M(<= 6),分别是学生人数和需要被查缴的物品种类数。第二行给出M个需要被查缴的物品编号,其中编号为4位数字。随后N行,每行给出一位学生的姓名缩写(由1-4个大写英文字母组成)、个人物品数量K(0 <= K <= 10)、以及K个物品的编号。 +>

    +>

    +> 输出格式: +>

    +>

    +> 顺次检查每个学生携带的物品,如果有需要被查缴的物品存在,则按以下格式输出该生的信息和其需要被查缴的物品的信息(注意行末不得有多余空格): +>

    +>
    姓名缩写: 物品编号1 物品编号2 ……
    +> 
    +>

    最后一行输出存在问题的学生的总人数和被查缴物品的总数。 +>

    +> 输入样例:
    +> 4 2
    +> 2333 6666
    +> CYLL 3 1234 2345 3456
    +> U 4 9966 6666 8888 6666
    +> GG 2 2333 7777
    +> JJ 3 0012 6666 2333
    +> 
    +> 输出样例:
    +> U: 6666 6666
    +> GG: 2333
    +> JJ: 6666 2333
    +> 3 5
    +> 
    +>
    + +## 思路 + +挺简单的,感觉仅有的难点反倒在控制输出格式上。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1072.c),欢迎交流 +```c +#include + +int main() +{ + int N, M, K, SNlist[10], SNtarget; + char name[5]; + + scanf("%d %d", &N, &M); + for(int i = 0; i < M; i ++) + scanf("%d", SNlist + i); + + int flag, student_total = 0, item_total = 0; + for(int i = 0; i < N; i ++) + { + flag = 0; + scanf("%s %d", name, &K); + for(int i = 0; i < K; i++) + { + scanf("%d", &SNtarget); + for(int i = 0 ; i < M; i ++) + { + if(SNtarget == SNlist[i]) + { + if(flag == 0) + { + flag = 1; + student_total++; + printf("%s:", name); + } + item_total++; + printf(" %04d", SNtarget); + } + } + } + if(flag) printf("\n"); + } + printf("%d %d", student_total, item_total); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1073.md b/_posts/2018-05-03-b1073.md new file mode 100644 index 0000000..7512000 --- /dev/null +++ b/_posts/2018-05-03-b1073.md @@ -0,0 +1,152 @@ +--- +layout: post +title: "1073. 多选题常见计分法(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    批改多选题是比较麻烦的事情,有很多不同的计分方法。有一种最常见的计分方法是:如果考生选择了部分正确选项,并且没有选择任何错误选项,则得到50%分数;如果考生选择了任何一个错误的选项,则不能得分。本题就请你写个程序帮助老师批改多选题,并且指出哪道题的哪个选项错的人最多。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出两个正整数N(<=1000)和M(<=100),分别是学生人数和多选题的个数。随后M行,每行顺次给出一道题的满分值(不超过5的正整数)、选项个数(不少于2且不超过5的正整数)、正确选项个数(不超过选项个数的正整数)、所有正确选项。注意每题的选项从小写英文字母a开始顺次排列。各项间以1个空格分隔。最后N行,每行给出一个学生的答题情况,其每题答案格式为“(选中的选项个数 选项1 ……)”,按题目顺序给出。注意:题目保证学生的答题情况是合法的,即不存在选中的选项数超过实际选项数的情况。 +>

    +>

    +> 输出格式: +>

    +>

    +> 按照输入的顺序给出每个学生的得分,每个分数占一行,输出小数点后1位。最后输出错得最多的题目选项的信息,格式为:“错误次数 题目编号(题目按照输入的顺序从1开始编号)-选项号”。如果有并列,则每行一个选项,按题目编号递增顺序输出;再并列则按选项号递增顺序输出。行首尾不得有多余空格。如果所有题目都没有人错,则在最后一行输出“Too simple”。 +>

    +> 输入样例1:
    +> 3 4 
    +> 3 4 2 a c
    +> 2 5 1 b
    +> 5 3 2 b c
    +> 1 5 4 a b d e
    +> (2 a c) (3 b d e) (2 a c) (3 a b e)
    +> (2 a c) (1 b) (2 a b) (4 a b d e)
    +> (2 b d) (1 e) (1 c) (4 a b c d)
    +> 
    +> 输出样例1:
    +> 3.5
    +> 6.0
    +> 2.5
    +> 2 2-e
    +> 2 3-a
    +> 2 3-b
    +> 
    +> 输入样例2:
    +> 2 2 
    +> 3 4 2 a c
    +> 2 5 1 b
    +> (2 a c) (1 b)
    +> (2 a c) (1 b)
    +> 
    +> 输出样例2:
    +> 5.0
    +> 5.0
    +> Too simple
    +> 
    +>
    + +## 思路 + +这一道题和[1058题](http://www.jianshu.com/p/4a8a570e41c3)是很类似的,但是稍微复杂一点。 + +一样的方面详见上面链接,下面只说1058题中没有的两点。 +- 正确性的判断。这道题中需要判断“没有全部选对”的情况,和1058题的解法一样,我使用按位存储的方法记录题目的答案,如选项A、C和D则记录为01101=13。那么分析方法为: + - **正确**:`学生选项==正确答案`,显然正确,为了后面方便,计算**`学生选项<异或>正确答案==0`**。 + - **错误**:`学生选项<异或>正确答案`这个数的每一位实际上表示学生在这一选项上是否正确,如正确答案为BC(00110),学生选了AC(00101),那么两者异或值为00011,即表示AB两选项出现错误。分析起来就是: + - 存在正确答案是0的位,上述异或值为1这样的情况,则说明学生有多选错误选项 + - 正确答案是0的位,异或值也全是0,则学生只选择了部分正确答案 + + 判断这两者的方法就是将异或值与正确答案取逻辑或,如果这个结果大于正确答案,则说明有异或值为1而正确答案为0的位,如果二者一样,说明没有。 + + 我的方法可能理解起来比较困难(当然大多是因为我表述不好),但是我觉得算是提供一个新的视角,供大家借鉴。 + +- 选项的错误次数 + 这个就比较简单了,我是用的int[M][5]矩阵记录每一道题每一个选项错误的次数,最后就好处理了。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1073.c),欢迎交流 +```c +#include + +#define MAX_M 100 +#define MAX_OPTIONS 5 + +int readanswer() +{ + char c; + int answer = 0, count; + + scanf("%d", &count); + for(int i = 0; i < count; i++) + { + while((c = getchar()) == ' '); + answer |= 1 << (c - 'a'); + } + return answer; +} + +int main() +{ + int N, M, full_score[MAX_M] = {0}, correct_ans[MAX_M] = {0}, + wrong_ans[MAX_M] = {0}, wrong_count[MAX_M][5] = {{0}}, + wrong_count_max = 0; + + scanf("%d %d", &N, &M); + /* Read M lines */ + int count_options; + for(int i = 0; i < M; i++) + { + scanf("%d %d", full_score + i, &count_options); + correct_ans[i] = readanswer(); + } + + /* Read N lines */ + for(int i = 0; i < N; i++) + { + float score = 0; + int answer; + for(int j = 0; j < M; j++) + { + while(getchar() != '('); + answer = readanswer(); + wrong_ans[j] = answer ^ correct_ans[j]; + + if(wrong_ans[j] == 0) /* all correct */ + score += full_score[j]; + else if((wrong_ans[j] | correct_ans[j]) == correct_ans[j]) + score += 0.5 * full_score[j]; /* partially corrent */ + + /* For every option, record the number of students got wrong */ + for(int k = 0; k < MAX_OPTIONS; k++) + { + wrong_count[j][k] += wrong_ans[j] >> k & 1; /* k-th bit */ + if(wrong_count[j][k] > wrong_count_max) + wrong_count_max = wrong_count[j][k]; + } + + while(getchar() != ')'); + } + printf("%.1f\n", score); + } + + if(wrong_count_max == 0) + printf("Too simple"); + else + for(int i = 0; i < M; i ++) + for(int j = 0; j < MAX_OPTIONS; j++) + if(wrong_count[i][j] == wrong_count_max) + printf("%d %d-%c\n", wrong_count_max, i + 1, j + 'a'); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1074.md b/_posts/2018-05-03-b1074.md new file mode 100644 index 0000000..2593a75 --- /dev/null +++ b/_posts/2018-05-03-b1074.md @@ -0,0 +1,95 @@ +--- +layout: post +title: "1074. 宇宙无敌加法器(20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的。而在PAT星人开挂的世界里,每个数字的每一位都是不同进制的,这种神奇的数字称为“PAT数”。每个PAT星人都必须熟记各位数字的进制表,例如“……0527”就表示最低位是7进制数、第2位是2进制数、第3位是5进制数、第4位是10进制数,等等。每一位的进制d或者是0(表示十进制)、或者是[2,9]区间内的整数。理论上这个进制表应该包含无穷多位数字,但从实际应用出发,PAT星人通常只需要记住前20位就够用了,以后各位默认为10进制。 +>

    +>

    +> 在这样的数字系统中,即使是简单的加法运算也变得不简单。例如对应进制表“0527”,该如何计算“6203+415”呢?我们得首先计算最低位:3+5=8;因为最低位是7进制的,所以我们得到1和1个进位。第2位是:0+1+1(进位)=2;因为此位是2进制的,所以我们得到0和1个进位。第3位是:2+4+1(进位)=7;因为此位是5进制的,所以我们得到2和1个进位。第4位是:6+1(进位)=7;因为此位是10进制的,所以我们就得到7。最后我们得到:6203+415=7201。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入首先在第一行给出一个N位的进制表(0 < N <=20),以回车结束。 +> 随后两行,每行给出一个不超过N位的非负的PAT数。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出两个PAT数之和。 +>

    +> 输入样例:
    +> 30527
    +> 06203
    +> 415
    +> 
    +> 输出样例:
    +> 7201
    +> 
    +>
    + +## 思路 + +这种题目在数据存储上有很多种方法,我这里用了我觉得写起来简单一点的。 + +使用三个字符串读取三个数。说实话如果少于18位,就可以用64位整型了,题目可能考虑到了这一点。。。 +按部就班从低位开始加,唯一要注意的就是角标到底是多少,要从后向前数。然后和每一位的进制比较,决定是否有进位。 +输出的时候,要注意是否照顾到了和为0的情况。比如我用字符串存储A+B,寻找第一个非零字符(0和'0'字符都算),开始输出,但如果全是0,就不会有输出了,要考虑这个情况。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1074.c),欢迎交流 +```c +#include +#include + +int main() +{ + int nBase, nA, nB, nS = 21, base, a, b; + char sBase[21] = {0}, sA[21] = {0}, sB[21] = {0}, sSum[22] = {0}; + + /* Read base table, number A and B into strings */ + scanf("%s %s %s", sBase, sA, sB); + + nBase = strlen(sBase); + nA = strlen(sA); + nB = strlen(sB); + + for(int i = 0; i < nBase; i++) + { + /* Transform corresponding digit to integers */ + a = nA <= i ? 0 : sA[nA - i - 1] - '0'; + b = nB <= i ? 0 : sB[nB - i - 1] - '0'; + base = sBase[nBase - i - 1] == '0' ? 10 : sBase[nBase - i - 1] - '0'; + + /* Calculate ith digit A + B, temperately store integers here */ + sSum[nS - i - 1] += a + b; + sSum[nS - i - 2] += sSum[nS - i - 1] / base; /* carry */ + sSum[nS - i - 1] = sSum[nS - i - 1] % base; + } + + /* Change to char type */ + for(int i = 0; i < 21; i++) + sSum[i] += '0'; + + /* find the first non-zero bit */ + for(int first = 0; first < 21; first++) + if(sSum[first] != 0 && sSum[first] != '0') + { + puts(sSum + first); + return 0; + } + + /* If A + B = 0, then all bits are zero */ + printf("0"); + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1075.md b/_posts/2018-05-03-b1075.md new file mode 100644 index 0000000..1fb3548 --- /dev/null +++ b/_posts/2018-05-03-b1075.md @@ -0,0 +1,99 @@ +--- +layout: post +title: "1075. 链表元素分类(25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而[0, K]区间内的元素都排在大于K的元素前面。但每一类内部元素的顺序是不能改变的。例如:给定链表为 18→7→-4→0→5→-6→10→11→-2,K为10,则输出应该为 -4→-6→-2→7→0→5→10→18→11。

    +>

    +> 输入格式: +>

    +>

    每个输入包含1个测试用例。每个测试用例第1行给出:第1个结点的地址;结点总个数,即正整数N (<= 105);以及正整数K (<=1000)。结点的地址是5位非负整数,NULL地址用-1表示。

    +>

    接下来有N行,每行格式为:

    +>

    Address Data Next

    +>

    其中Address是结点地址;Data是该结点保存的数据,为[-105, 105]区间内的整数;Next是下一结点的地址。题目保证给出的链表不为空。

    +>

    +> 输出格式: +>

    +>

    对每个测试用例,按链表从头到尾的顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。

    +> 输入样例:
    +> 00100 9 10
    +> 23333 10 27777
    +> 00000 0 99999
    +> 00100 18 12309
    +> 68237 -6 23333
    +> 33218 -4 00000
    +> 48652 -2 -1
    +> 99999 5 68237
    +> 27777 11 48652
    +> 12309 7 33218
    +> 
    +> 输出样例:
    +> 33218 -4 68237
    +> 68237 -6 48652
    +> 48652 -2 12309
    +> 12309 7 00000
    +> 00000 0 99999
    +> 99999 5 23333
    +> 23333 10 00100
    +> 00100 18 27777
    +> 27777 11 -1
    +> 
    +>
    + +## 思路 + +这道题和[1025 反转连表](http://www.jianshu.com/p/f8ba1e37a8c8)是差不多的(所以同学们,刷题真的管用啊哈哈)。 + +这道题用了和1025题不一样的方法,1025我使用数量刚刚好的数组存储,对链表进行了整理,然后数组顺序就是链表顺序。这道题我直接开了10^5的数组,把角标当作地址,直接读取,是**拿空间换时间**的方法。 + +这样写起来就很简单了。 +- 开两个大数组,角标就是地址,一个储存数据,一个存储链表下一个节点的地址。 +- 三次遍历链表,依次输出符合要求的结点。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1075.c),欢迎交流 +```c +#include + +int main() +{ + int data[100000] = {0}, next[100000] = {0}, + firstaddr, addr, lastaddr = -1, N, K; + + /* Read */ + scanf("%d %d %d", &firstaddr, &N, &K); + for(int i = 0; i < N; i++) + { + scanf("%d", &addr); + scanf("%d %d", data + addr, next + addr); + } + + /* Scan three times */ + for(int i = 0; i < 3; i++) + { + /* Scan along the linked list */ + for(addr = firstaddr; addr != -1; addr = next[addr]) + { + /* If the number fits the condition */ + if((i == 0 && data[addr] < 0) + || (i == 1 && 0 <= data[addr] && data[addr] <= K) + || (i == 2 && data[addr] > K)) + { + /* Print */ + if(lastaddr != -1) printf(" %05d\n", addr); + printf("%05d %d", addr, data[addr]); + lastaddr = addr; + } + } + } + printf(" -1"); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1076.md b/_posts/2018-05-03-b1076.md new file mode 100644 index 0000000..f43da1a --- /dev/null +++ b/_posts/2018-05-03-b1076.md @@ -0,0 +1,68 @@ +--- +layout: post +title: "1076. Wifi密码 (15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    下面是微博上流传的一张照片:“各位亲爱的同学们,鉴于大家有时需要使用wifi,又怕耽误亲们的学习,现将wifi密码设置为下列数学题答案:A-1;B-2;C-3;D-4;请同学们自己作答,每两日一换。谢谢合作!!~”—— 老师们为了促进学生学习也是拼了…… 本题就要求你写程序把一系列题目的答案按照卷子上给出的对应关系翻译成wifi的密码。这里简单假设每道选择题都有4个选项,有且只有1个正确答案。 +>

    +>
    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数N(<= 100),随后N行,每行按照“编号-答案”的格式给出一道题的4个选项,“T”表示正确选项,“F”表示错误选项。选项间用空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中输出wifi密码。 +>

    +> 输入样例:
    +> 8
    +> A-T B-F C-F D-F
    +> C-T B-F A-F D-F
    +> A-F D-F C-F B-T
    +> B-T A-F C-F D-F
    +> B-F D-T A-F C-F
    +> A-T C-F B-F D-F
    +> D-T B-F C-F A-F
    +> C-T A-F B-F D-F
    +> 
    +> 输出样例:
    +> 13224143
    +> 
    +>
    + +## 思路 + +这道题很简单。 + +单纯的思路就是分N次,每次读取4次的X-Y形式的字符串,判断Y是否是T,是的话根据输出1234其中一个。 + +“作弊的思路”:由于题目的输入格式限制(每一题有且只有一个正确答案),实际上不需要管有几题,每题几个选项。就一次一次暴力读取(每次最多3字符,甚至可以纳入第一行的数字),只要后面是T就输出就没问题了。 + +这种思路就是纯粹只能用于这种竞赛题目中的技巧,因此没有广泛的适用性。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1076.c),欢迎交流 +```c +#include + +int main() +{ + char string[4]; + + while(scanf("%s", string) != EOF) + if(string[2] == 'T') + putchar("1234"[string[0] - 'A']); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1077.md b/_posts/2018-05-03-b1077.md new file mode 100644 index 0000000..6640f7d --- /dev/null +++ b/_posts/2018-05-03-b1077.md @@ -0,0 +1,89 @@ +--- +layout: post +title: "1077. 互评成绩计算 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    在浙大的计算机专业课中,经常有互评分组报告这个环节。一个组上台介绍自己的工作,其他组在台下为其表现评分。最后这个组的互评成绩是这样计算的:所有其他组的评分中,去掉一个最高分和一个最低分,剩下的分数取平均分记为 G1;老师给这个组的评分记为 G2。该组得分为 (G1+G2)/2,最后结果四舍五入后保留整数分。本题就要求你写个程序帮助老师计算每个组的互评成绩。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出两个正整数N(> 3)和M,分别是分组数和满分,均不超过100。随后N行,每行给出该组得到的N个分数(均保证为整型范围内的整数),其中第1个是老师给出的评分,后面 N-1 个是其他组给的评分。合法的输入应该是[0, M]区间内的整数,若不在合法区间内,则该分数须被忽略。题目保证老师的评分都是合法的,并且每个组至少会有3个来自同学的合法评分。 +>

    +>

    +> 输出格式: +>

    +>

    +> 为每个组输出其最终得分。每个得分占一行。 +>

    +> 输入样例:
    +> 6 50
    +> 42 49 49 35 38 41
    +> 36 51 50 28 -1 30
    +> 40 36 41 33 47 49
    +> 30 250 -25 27 45 31
    +> 48 0 0 50 50 1234
    +> 43 41 36 29 42 29
    +> 
    +> 输出样例:
    +> 42
    +> 33
    +> 41
    +> 31
    +> 37
    +> 39
    +> 
    +>
    + +## 思路 + +挺中规中矩的简单题 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1077.c),欢迎交流 +```c +#include + +int main() +{ + int N, M, count; + double G1, G2, Gsum, Gmax, Gmin, G; + scanf("%d %d", &N, &M); + + for(int i = 0; i < N; i++) + { + scanf("%lf", &G2); + + /* Calculate G1 */ + Gsum = 0; + count = 0; + Gmin = M; + Gmax = 0; + for(int j = 0; j < N - 1; j++) + { + scanf("%lf", &G); + if(G >= 0 && G <= M) + { + count++; + Gsum += G; + if(G > Gmax) /* Record highest score */ + Gmax = G; + if(G < Gmin) /* Record lowest score */ + Gmin = G; + } + } + G1 = (Gsum - Gmax - Gmin) / (count - 2); /* Average */ + + printf("%d\n", (int)((G1 + G2 + 1) / 2)); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1078.md b/_posts/2018-05-03-b1078.md new file mode 100644 index 0000000..87a036c --- /dev/null +++ b/_posts/2018-05-03-b1078.md @@ -0,0 +1,125 @@ +--- +layout: post +title: "1078. 字符串压缩与解压 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    文本压缩有很多种方法,这里我们只考虑最简单的一种:把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示。例如 ccccc 就用 5c 来表示。如果字符没有重复,就原样输出。例如 aba 压缩后仍然是 aba。 +>

    +>

    +> 解压方法就是反过来,把形如 5c 这样的表示恢复为 ccccc。 +>

    +>

    +> 本题需要你根据压缩或解压的要求,对给定字符串进行处理。这里我们简单地假设原始字符串是完全由英文字母和空格组成的非空字符串。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个字符,如果是 C 就表示下面的字符串需要被压缩;如果是 D 就表示下面的字符串需要被解压。第二行给出需要被压缩或解压的不超过1000个字符的字符串,以回车结尾。题目保证字符重复个数在整型范围内,且输出文件不超过1MB。 +>

    +>

    +> 输出格式: +>

    +>

    +> 根据要求压缩或解压字符串,并在一行中输出结果。 +>

    +> 输入样例 1:
    +> C
    +> TTTTThhiiiis isssss a   tesssst CAaaa as
    +> 
    +> 输出样例 1:
    +> 5T2h4is i5s a3 te4st CA3a as
    +> 
    +> 输入样例 2:
    +> D
    +> 5T2h4is i5s a3 te4st CA3a as10Z
    +> 
    +> 输出样例 2:
    +> TTTTThhiiiis isssss a   tesssst CAaaa asZZZZZZZZZZ
    +> 
    +>
    + +## 思路 + +也不是很难,压缩和解压是独立的,我写了两个函数 + +- compress:压缩。记录当前字符和前一个字符,不一样时就说明需要总结前一个字符的出现次数,并且输出。在我的实现里,借用最后的换行符为最后一个字符,就无须处理末尾的特殊情况。 +- decompress:解压。同样用一个计数变量记录需要输出的次数,由于我采用的是逐一读取字符的方法,所以如果次数大于10就要注意把多位数加在一起(如题目中10Z)。遇到数字就改变计数变量,遇到其他字符就输出。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1078.c),欢迎交流 +```c +#include + +void compress() +{ + char previous = getchar(), current; + int count = 1; + + while((current = getchar())) + { + if(current == previous) + { + count++; /* Increament count */ + } + else + { + if(count > 1) /* Only print if count > 1 */ + printf("%d", count); + putchar(previous); + previous = current; + count = 1; + } + + if(current == '\n') /* Don't put this in while() */ + break; + } +} + +void decompress() +{ + char c; + int count = 0; + + while((c = getchar()) != '\n') + { + if(c >= '0' && c <= '9') /* If it is number */ + { + count = count * 10 + c - '0'; /* Accumulate count */ + } + else /* If it is not number */ + { + if(count == 0) /* No number before char */ + count = 1; /* print once */ + for(int i = 0; i < count; i++) /* Or print 'count' times */ + putchar(c); + count = 0; /* Reset 'count' */ + } + } +} + +int main() +{ + switch(getchar()) + { + case 'C': + while(getchar() != '\n'); + compress(); + break; + case 'D': + while(getchar() != '\n'); + decompress(); + break; + default: + break; + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1079.md b/_posts/2018-05-03-b1079.md new file mode 100644 index 0000000..58974bb --- /dev/null +++ b/_posts/2018-05-03-b1079.md @@ -0,0 +1,143 @@ +--- +layout: post +title: "1079. 延迟的回文数 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    给定一个 k+1 位的正整数 N,写成 ak...a1a0 的形式,其中对所有 i 有 0 <= ai < 10 且 ak > 0。N 被称为一个回文数,当且仅当对所有 i 有 ai = ak-i。零也被定义为一个回文数。 +>

    +>

    +> 非回文数也可以通过一系列操作变出回文数。首先将该数字逆转,再将逆转数与该数相加,如果和还不是一个回文数,就重复这个逆转再相加的操作,直到一个回文数出现。如果一个非回文数可以变出回文数,就称这个数为延迟的回文数。(定义翻译自 https://en.wikipedia.org/wiki/Palindromic_number) +>

    +>

    +> 给定任意一个正整数,本题要求你找到其变出的那个回文数。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在一行中给出一个不超过1000位的正整数。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对给定的整数,一行一行输出其变出回文数的过程。每行格式如下 +>

    +> A + B = C
    +> 
    +>

    +> 其中A是原始的数字,B是A的逆转数,C是它们的和。A从输入的整数开始。重复操作直到C在10步以内变成回文数,这时在一行中输出“C is a palindromic number.”;或者如果10步都没能得到回文数,最后就在一行中输出“Not found in 10 iterations.”。 +>

    +> 输入样例 1:
    +> 97152
    +> 
    +> 输出样例 1:
    +> 97152 + 25179 = 122331
    +> 122331 + 133221 = 255552
    +> 255552 is a palindromic number.
    +> 
    +> 输入样例 2:
    +> 196
    +> 
    +> 输出样例 2:
    +> 196 + 691 = 887
    +> 887 + 788 = 1675
    +> 1675 + 5761 = 7436
    +> 7436 + 6347 = 13783
    +> 13783 + 38731 = 52514
    +> 52514 + 41525 = 94039
    +> 94039 + 93049 = 187088
    +> 187088 + 880781 = 1067869
    +> 1067869 + 9687601 = 10755470
    +> 10755470 + 07455701 = 18211171
    +> Not found in 10 iterations.
    +> 
    +>
    + +## 思路 + +这道题写起来有一个小点令我很头疼,不过总体来说还是很简单。 + +整体思路就是,循环: +- 把A反转,得到B +- 相加A和B,得到C +- 判断C是否是回文数 + +为了包含进A本身就是回文数的特殊情况,可以在第一步判断A是否是回文数。 + +然后就是具体的每一步的实现: +- 翻转一个数,`reverseAtoB`,将A翻转,结果存储在B中,实现很简单。 + - 不过要说一点,我并没有每次在B字符串的末尾加上`'\0'`,是因为我初始化全为`\0`,并且此题的情况中字符串不会缩短,因此不加不会有错误,但是在更广泛的用途中极有可能产生bug。 +- 两数相加,`addBtoA`,将A和B相加,结果存储在A中,这个就是让我有点头疼的。由于题目要求数字只能用字符串存储,那么原地做加法的话,最后如果要进位,前面就没有位置了啊。最后还是硬着头皮暴力地把所有位后移(使用了`memmove`函数),因此看起来并不是很简洁。 +- 判断是否是回文数,`isPalindromicNumber`,也很简单。 + +简单的说一下为什么字符串长度用1011: +初始数字最多1000位,每次翻转相加,对于任何数都不会增大10倍以上(如10009+90001=100010,很接近10倍),因此10次之内得到的结果绝对不会超出1010位,因此字符串就用了char[1011]。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1079.c),欢迎交流 +```c +#include +#include + +int isPalindromicNumber(char n[]) +{ + int len = strlen(n); + for(int i = 0; i < len / 2; i++) + if(n[i] != n[len - i - 1]) + return 0; + return 1; +} + +void addBtoA(char a[], char b[]) +{ + /* Assume length of a and b are the same */ + int len = strlen(a), sum, carry = 0; + for(int i = len - 1; i >= 0; i --) + { + sum = a[i] - '0' + b[i] - '0' + carry; + a[i] = sum % 10 + '0'; + carry = sum / 10; + } + if(carry) /* Length of a + b is larger than a or b */ + { + memmove(a + 1, a, len + 1); /* Shift to right by 1 */ + a[0] = carry + '0'; /* Add the carry to beginning */ + } +} + +void reverseAtoB(char a[], char b[]) +{ + int len = strlen(a); + for(int i = 0; i < len; i++) + b[len - i - 1] = a[i]; +} + +int main() +{ + int i; + char a[1011] = {0}, b[1011] = {0}; + + scanf("%s", a); + for (i = 0; i < 10 && !isPalindromicNumber(a); i++) + { + reverseAtoB(a, b); + printf("%s + %s = ", a, b); + addBtoA(a, b); + printf("%s\n", a); + } + + if(i == 10) + printf("Not found in 10 iterations."); + else + printf("%s is a palindromic number.", a); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1080.md b/_posts/2018-05-03-b1080.md new file mode 100644 index 0000000..f702af5 --- /dev/null +++ b/_posts/2018-05-03-b1080.md @@ -0,0 +1,181 @@ +--- +layout: post +title: "1080. MOOC期终成绩 (25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    +> 对于在中国大学MOOC(http://www.icourse163.org/)学习“数据结构”课程的学生,想要获得一张合格证书,必须首先获得不少于200分的在线编程作业分,然后总评获得不少于60分(满分100)。总评成绩的计算公式为 G = (G期中x 40% + G期末x 60%),如果 G期中 > G期末;否则总评 G 就是 G期末。这里 G期中 和 G期末 分别为学生的期中和期末成绩。 +>

    +>

    +> 现在的问题是,每次考试都产生一张独立的成绩单。本题就请你编写程序,把不同的成绩单合为一张。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行给出3个整数,分别是 P(做了在线编程作业的学生数)、M(参加了期中考试的学生数)、N(参加了期末考试的学生数)。每个数都不超过10000。 +>

    +>

    +> 接下来有三块输入。第一块包含 P 个在线编程成绩 G编程;第二块包含 M 个期中考试成绩 G期中;第三块包含 N 个期末考试成绩 G期末。每个成绩占一行,格式为:学生学号 分数。其中学生学号为不超过20个字符的英文字母和数字;分数是非负整数(编程总分最高为900分,期中和期末的最高分为100分)。 +>

    +>

    +> 输出格式: +>

    +>

    +> 打印出获得合格证书的学生名单。每个学生占一行,格式为: +>

    +>

    +> 学生学号 G编程 G期中 G期末 G +>

    +>

    +> 如果有的成绩不存在(例如某人没参加期中考试),则在相应的位置输出“-1”。输出顺序为按照总评分数(四舍五入精确到整数)递减。若有并列,则按学号递增。题目保证学号没有重复,且至少存在1个合格的学生。 +>

    +> 输入样例:
    +> 6 6 7
    +> 01234 880
    +> a1903 199
    +> ydjh2 200
    +> wehu8 300
    +> dx86w 220
    +> missing 400
    +> ydhfu77 99
    +> wehu8 55
    +> ydjh2 98
    +> dx86w 88
    +> a1903 86
    +> 01234 39
    +> ydhfu77 88
    +> a1903 66
    +> 01234 58
    +> wehu8 84
    +> ydjh2 82
    +> missing 99
    +> dx86w 81
    +> 
    +> 输出样例:
    +> missing 400 -1 99 99
    +> ydjh2 200 98 82 88
    +> dx86w 220 88 81 84
    +> wehu8 300 55 84 84
    +> 
    +>
    + +## 思路 + +要是C语言有map就省事多了~~第一次的时候使用暴力的for循环查找([代码在这](https://github.com/OliverLew/PAT/blob/fd1f42309b79b52313b131d5738eaba1adb8841d/PATBasic/1080.c),其实没必要看的哈),结果超时了`/(ㄒoㄒ)/~~`,估计不超时也是错的,所以改用排序再二分查找的方法。 +(自己写hash table实在无力,排下序能从C标准库里找到相关函数(`qsort`、`bsearch`),还是很傻瓜的改进方法哈哈) + +整体思路: +- 先读取在线编程成绩,只需记录200以上的,筛掉不及格的。 +- 按照学号排序(`qsort`)。 +- 读取期中考试成绩。使用二分查找(`bsearch`)找到该学号的记录并更新。找不到(返回`NULL`)的说明没有编程成绩,也就无需做什么了。 +- 读取期末考试成绩,并计算总评。 +- 按总评降序和学号升序排序。 +- 依次输出总评60及以上的成绩。 + +写qsort/bsearch的比较(cmp)函数时,真的能检验对指针的理解程度 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1080.c),欢迎交流 +```c +#include +#include +#include + +typedef struct { + char name[21]; + int programming; + int midterm; + int finalexam; + int total_mark; +} Score; + +int cmp_sort_total(const void *a, const void *b) +{ + Score *s1 = *(Score**)a, *s2 = *(Score**)b; + if(s2->total_mark - s1->total_mark) + return s2->total_mark - s1->total_mark; + return strcmp(s1->name, s2->name); +} + +int cmp_sort_name(const void *a, const void *b) +{ + Score *s1 = *(Score**)a, *s2 = *(Score**)b; + return strcmp(s1->name, s2->name); +} + +int cmp_bsearch(const void *strptr, const void *scoreptr) +{ + Score *s = *(Score**)scoreptr; + char *name = (char*)strptr; + return strcmp(name, s->name); +} + +int main() +{ + int P, M, N; + scanf("%d %d %d", &P, &M, &N); + + int score, count = 0; + char name[21]; + Score buf[10000], *scores[10000] = {0}, *s = buf; + + for(int i = 0; i < P; i++) /* Read programming grade */ + { + scanf("%s %d", name, &score); + if(score >= 200) /* Only record if score >= 200 */ + { + strcpy(s->name, name); + s->programming = score; + s->midterm = -1; + s->finalexam = -1; + s->total_mark = 0; + scores[count++] = s++; + } + } + + /* Sort by name for future searchings using bsearch */ + qsort(scores, count, sizeof(Score*), cmp_sort_name); + + void *result; + for(int i = 0; i < M; i++) /* Read midterm grade */ + { + scanf("%s %d", name, &score); + result = bsearch(name, scores, count, sizeof(Score*), cmp_bsearch); + if(result != NULL) /* If name is in the list, then record */ + (*(Score**)result)->midterm = score; + } + + for(int i = 0; i < N; i++) /* Read final exam grade */ + { + scanf("%s %d", name, &score); + result = bsearch(name, scores, count, sizeof(Score*), cmp_bsearch); + if(result != NULL) + { + s = *(Score**)result; + s->finalexam = score; + + /* Calculate total mark */ + if(s->finalexam >= s->midterm) /* final exam grade higher */ + s->total_mark = s->finalexam; + else /* midterm grade higher */ + s->total_mark = 0.6 * s->finalexam + 0.4 * s->midterm + 0.5; + } + } + + /* Sort by total mark */ + qsort(scores, count, sizeof(Score*), cmp_sort_total); + + for(Score **p = scores; *p && (*p)->total_mark >= 60; p++) + printf("%s %d %d %d %d\n", (*p)->name, (*p)->programming, + (*p)->midterm, (*p)->finalexam, (*p)->total_mark); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1081.md b/_posts/2018-05-03-b1081.md new file mode 100644 index 0000000..ba307d8 --- /dev/null +++ b/_posts/2018-05-03-b1081.md @@ -0,0 +1,91 @@ +--- +layout: post +title: "1081. 检查密码 (15)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    本题要求你帮助某网站的用户注册模块写一个密码合法性检查的小功能。该网站要求用户设置的密码必须由不少于6个字符组成,并且只能有英文字母、数字和小数点".",还必须既有字母也有数字。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数 N(<=100),随后 N 行,每行给出一个用户设置的密码,为不超过80个字符的非空字符串,以回车结束。 +>

    +>

    +> 输出格式: +>

    +>

    +> 对每个用户的密码,在一行中输出系统反馈信息,分以下5种: +>

    +>
      +>
    • 如果密码合法,输出“Your password is wan mei.”; +>
    • 如果密码太短,不论合法与否,都输出“Your password is tai duan le.”; +>
    • 如果密码长度合法,但存在不合法字符,则输出“Your password is tai luan le.”; +>
    • 如果密码长度合法,但只有字母没有数字,则输出“Your password needs shu zi.”; +>
    • 如果密码长度合法,但只有数字没有字母,则输出“Your password needs zi mu.”。 +>
    +> 输入样例:
    +> 5
    +> 123s
    +> zheshi.wodepw
    +> 1234.5678
    +> WanMei23333
    +> pass*word.6
    +> 
    +> 输出样例:
    +> Your password is tai duan le.
    +> Your password needs shu zi.
    +> Your password needs zi mu.
    +> Your password is wan mei.
    +> Your password is tai luan le.
    +> 
    +>
    + +## 思路 + +没有什么难度,确定好哪种情况优先级高就行了。优先级依次为: + +1. 密码太短 +2. 存在不合法字符 +3. 缺少数字或者字母 + +想吐槽的就是题目不很严谨啊,全是小数点就合法了吗? + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1081.c),欢迎交流 +```c +#include +#include + +int main() +{ + char c; + int N, count, num_digit, num_alpha, num_else; + + scanf("%d", &N); + while(getchar() == '\n'); + for(int i = 0; i < N; i++) + { + num_digit = num_alpha = num_else = 0; + for(count = 0; (c = getchar()) != '\n'; count++) + { + if(isdigit(c)) num_digit++; + else if(isalpha(c)) num_alpha++; + else if(c != '.') num_else++; + } + if(count < 6) puts("Your password is tai duan le."); + else if(num_else) puts("Your password is tai luan le."); + else if(num_alpha && !num_digit) puts("Your password needs shu zi."); + else if(num_digit && !num_alpha) puts("Your password needs zi mu."); + else puts("Your password is wan mei."); + } + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1082.md b/_posts/2018-05-03-b1082.md new file mode 100644 index 0000000..d9a64ad --- /dev/null +++ b/_posts/2018-05-03-b1082.md @@ -0,0 +1,77 @@ +--- +layout: post +title: "1082. 射击比赛 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    本题目给出的射击比赛的规则非常简单,谁打的弹洞距离靶心最近,谁就是冠军;谁差得最远,谁就是菜鸟。本题给出一系列弹洞的平面坐标(x,y),请你编写程序找出冠军和菜鸟。我们假设靶心在原点(0,0)。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入在第一行中给出一个正整数 N(<= 10 000)。随后 N 行,每行按下列格式给出:

    +>
    +> ID x y
    +> 
    +>

    其中 ID 是运动员的编号(由4位数字组成);x 和 y 是其打出的弹洞的平面坐标(x,y),均为整数,且 0 <= |x|, |y| <= 100。题目保证每个运动员的编号不重复,且每人只打 1 枪。 +>

    +>

    +> 输出格式: +>

    +>

    +> 输出冠军和菜鸟的编号,中间空 1 格。题目保证他们是唯一的。 +>

    +> 输入样例:
    +> 3
    +> 0001 5 7
    +> 1020 -1 3
    +> 0233 0 -1
    +> 
    +> 输出样例:
    +> 0233 0001
    +> 
    +>
    + +## 思路 + +很简单,又是记录最大最小的问题,做到现在应该没有什么难度了 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1082.c),欢迎交流 +```c +#include + +int main() +{ + int best_id, worst_id, cur_id; + int x, y, N; + int min_dist_sq = 99999, max_dist_sq = -1, cur_dist_sq; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + scanf("%d %d %d", &cur_id, &x, &y); + cur_dist_sq = x * x + y * y; + if(min_dist_sq > cur_dist_sq) + { + min_dist_sq = cur_dist_sq; + best_id = cur_id; + } + if(max_dist_sq < cur_dist_sq) + { + max_dist_sq = cur_dist_sq; + worst_id = cur_id; + } + } + + printf("%04d %04d", best_id, worst_id); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1083.md b/_posts/2018-05-03-b1083.md new file mode 100644 index 0000000..7620b66 --- /dev/null +++ b/_posts/2018-05-03-b1083.md @@ -0,0 +1,69 @@ +--- +layout: post +title: "1083. 是否存在相等的差 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    给定 N 张卡片,正面分别写上 1、2、……、N,然后全部翻面,洗牌,在背面分别写上 1、2、……、N。将每张牌的正反两面数字相减(大减小),得到 N 个非负差值,其中是否存在相等的差? +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数 N(2 <= N <= 10000),随后一行给出 1 到 N 的一个洗牌后的排列,第 i 个数表示正面写了 i 的那张卡片背面的数字。 +>

    +>

    +> 输出格式: +>

    +>

    +> 按照“差值 重复次数”的格式从大到小输出重复的差值及其重复的次数,每行输出一个结果。 +>

    +> 输入样例:
    +> 8
    +> 3 5 8 6 2 1 4 7
    +> 
    +> 输出样例:
    +> 5 2
    +> 3 3
    +> 2 2
    +> 
    +>
    + +## 思路 + +这个和之前一些记录字符的题目相近,就是统计数字或者字符的出现次数或者其他相关信息。 + +数字出现的次序就是正面数字,数字本身就是背面数字,两者之差就是所需。 + +仅需注意两小点: +- 差值要取绝对值 +- 输出仅输出出现次数大于等于2的 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1083.c),欢迎交流 +```c +#include + +int main() +{ + int N, num, diff[10000] = {0}; + + scanf("%d", &N); + for(int i = 0; i < N; i++) + { /* The front is i + 1, the back is num */ + scanf("%d", &num); + diff[(num > i + 1) ? (num - i - 1) : (i + 1 - num)]++; + } + + for(int i = N - 1; i >= 0; i--) + if(diff[i] >= 2) + printf("%d %d\n", i, diff[i]); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1084.md b/_posts/2018-05-03-b1084.md new file mode 100644 index 0000000..713533a --- /dev/null +++ b/_posts/2018-05-03-b1084.md @@ -0,0 +1,103 @@ +--- +layout: post +title: "1084. 外观数列 (20)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    外观数列是指具有以下特点的整数序列:

    +> d, d1, d111, d113, d11231, d112213111, ...
    +> 

    +> 它从不等于 1 的数字 d 开始,序列的第 n+1 项是对第 n 项的描述。比如第 2 项表示第 1 项有 1 个 d,所以就是 d1;第 2 项是 1 个 d(对应 d1)和 1 个 1(对应 11),所以第 3 项就是 d111。又比如第 4 项是 d113,其描述就是 1 个 d,2 个 1,1 个 3,所以下一项就是 d11231。当然这个定义对 d = 1 也成立。本题要求你推算任意给定数字 d 的外观数列的第 N 项。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出[0,9]范围内的一个整数 d、以及一个正整数 N(<=40),用空格分隔。 +>

    +>

    +> 输出格式: +>

    +>

    +> 在一行中给出数字 d 的外观数列的第 N 项。 +>

    +> 输入样例:
    +> 1 8
    +> 
    +> 输出样例:
    +> 1123123111
    +> 
    +>
    + +## 思路 + +我说过遇到一些数学性比较强的题目会给出比较严谨的说明,看到这题,我要收回这句话。。 + +因为这道题我求不出一个比较小的字符串长度下限啊,根本不会分析。。。 + +不过幸好输入的可能情况很少,在自己写的时候尝试一下就好了。 + +**大致思路:** + +这道题要求解一个递推的序列,递推关系和上一个数中数字的出现模式相关。逻辑上还是好理解的,实现上也不是很难。我的思路是这样的: + +- 用两个字符串做存储,交替地存放新产生的递归序列 +- 用两个指针分别指向代表第k个和第k+1个序列的字符串,这样交替的时候交换字符串就可以了 +- 共N-1次(因为最初的算序列的第一个)扫描第k个字符串,同时生成第k+1个。具体为: + - 初始化计数变量 + - 从第一个字符开始向后遍历第k个字符串,直到最后一个字符 + - 我先增加了计数变量,因为我是从0开始计数的,因此任何情况都要增加计数 + - 比较该字符和后一个字符,看是否相等:相同的话,上面已增加过计数,什么都不做;不相同的话,向另一个字符串里记录该字符和出现次数,并重置计数 + + +**思考:** + +**关于字符串长度:** 这个题目很难分析需要多长的字符串,不过输入只可能有0-9十种数字和1-40的N值,因此只需让N=40时,字符串足够长就够了。我测试的结果是: + +- d = 1, N = 40: 结果长63139个字符 +- d取其它, N = 40: 结果长73393个字符 + +所以我就开了长度为100 000的字符串。 + +**关于算法的设计:** 这道题我第一感觉其实是不需要开数组,因为每次递归过程仅需要知道局部的信息即可,不需要对整体的信息进行整理。但是问题是这道题需要多次利用已生成的结果作为新的数据进行处理,所以一定要把结果保留下来才可以进行多次的递归。至少我是觉得这道题一定要开数组进行存储才可以。 + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1084.c),欢迎交流 +```c +#include + +int main() +{ + int N, count; + char string1[100000] = {0}, string2[100000] = {0}; + char *s1 = string1, *s2 = string2, *temp; + char *p1, *p2; + + scanf("%s %d", s1, &N); + + for(int i = 1; i < N; i++) /* Loop through nth string */ + { + for(p1 = s1, p2 = s2, count = 0; *p1; p1++) + { + count++; /* Magic, don't touch! */ + if(*p1 != *(p1 + 1)) /* New char or end */ + { + *p2++ = *p1; /* Record character */ + *p2++ = count + '0'; /* Record count */ + count = 0; /* Reset count */ + } + } + /* Swap */ + temp = s1, s1 = s2, s2 = temp; + } + + puts(s1); + + return 0; +} + +``` \ No newline at end of file diff --git a/_posts/2018-05-03-b1085.md b/_posts/2018-05-03-b1085.md new file mode 100644 index 0000000..8cd8c7d --- /dev/null +++ b/_posts/2018-05-03-b1085.md @@ -0,0 +1,161 @@ +--- +layout: post +title: "1085. PAT单位排行 (25)" +categories: PAT-B +tags: [PAT-B] +--- +## 题目 + +>
    +>

    每次 PAT 考试结束后,考试中心都会发布一个考生单位排行榜。本题就请你实现这个功能。 +>

    +>

    +> 输入格式: +>

    +>

    +> 输入第一行给出一个正整数N(<=105),即考生人数。随后N行,每行按下列格式给出一个考生的信息: +>

    +>

    +> 准考证号 得分 学校 +>

    +>

    +> 其中“准考证号”是由6个字符组成的字符串,其首字母表示考试的级别:“B”代表乙级,“A”代表甲级,“T”代表顶级;“得分”是 [0,100] 区间内的整数;“学校”是由不超过6个英文字母组成的单位码(大小写无关)。注意:题目保证每个考生的准考证号是不同的。 +>

    +>

    +> 输出格式: +>

    +>

    +> 首先在一行中输出单位个数。随后按以下格式非降序输出单位的排行榜: +>

    +>

    +> 排名 学校 加权总分 考生人数 +>

    +>

    +> 其中“排名”是该单位的排名(从1开始);“学校”是全部按小写字母输出的单位码;“加权总分”定义为“乙级总分/1.5 + 甲级总分 + 顶级总分*1.5”的整数部分;“考生人数”是该属于单位的考生的总人数。 +>

    +>

    +> 学校首先按加权总分排行。如有并列,则应对应相同的排名,并按考生人数升序输出。如果仍然并列,则按单位码的字典序输出。 +>

    +> 输入样例:
    +> 10
    +> A57908 85 Au
    +> B57908 54 LanX
    +> A37487 60 au
    +> T28374 67 CMU
    +> T32486 24 hypu
    +> A66734 92 cmu
    +> B76378 71 AU
    +> A47780 45 lanx
    +> A72809 100 pku
    +> A03274 45 hypu
    +> 
    +> 输出样例:
    +> 5
    +> 1 cmu 192 2
    +> 1 au 192 3
    +> 3 pku 100 1
    +> 4 hypu 81 2
    +> 4 lanx 81 2
    +> 
    +>
    + +## 思路 + +又是这种结构体排序的题目,刷完题就觉得PATBasic套路还不算深,题型还蛮固定的 + +**大致思路:** + +- 用两个结构,一个保存学生的学号,成绩和所属学校,另一个保存学校的名称,成绩和学生人数 +- 依然是用指针操作,排序的时候避免移动结构体本身 +- 读取学生信息到学生结构数组,此时将学校名转换为小写 +- 然后按照学生的学校名称排序,方便计算学校相关数据 +- 遍历所有学生,将相同学校的学生的信息汇总,包括总成绩可人数。这一部分实际上和1084题是一样的,即统计相邻相同的对象的数据,可以对比两个代码,结构是一样的。这里面就有一点要注意: + - 分数的计算,注意要加权,过程用浮点数,最后取整 +- 最后再按照题目要求排序 +- 输出,我是在输出的时候计算名次的,简单的说就是分数一样的时候不去更新,分数不一样的时候,名次改为序号即可(因为排过序了) + +## 代码 + +[最新代码@github](https://github.com/OliverLew/PAT/blob/master/PATBasic/1085.c),欢迎交流 +```c +#include +#include +#include +#include + +typedef struct {char id[7], school[7]; int score; } Student; +typedef struct {char name[7]; int score, num; } School; + +int cmp_school(const void *a, const void *b) +{ + return strcmp((*(Student**)a)->school, (*(Student**)b)->school); +} + +int cmp_sort_all(const void *a, const void *b) +{ + School *s1 = *(School**)a, *s2 = *(School**)b; + if(s1->score - s2->score) + return s2->score - s1->score; + else if(s1->num - s2->num) + return s1->num - s2->num; + else + return strcmp(s1->name, s2->name); +} + +int main() +{ + int N, M = 0, num = 0; + double total_score = 0; + Student students[100001] = {0}, *stu[100001] = {0}; + School schools[100001] = {0}, *sch[100001] = {0}; + + /* Read into an array */ + scanf("%d", &N); + for(int i = 0; i < N; i++) + { + stu[i] = students + i; /* Initialize pointer array */ + scanf("%s %d %s", stu[i]->id, &stu[i]->score, stu[i]->school); + for(char *s = stu[i]->school; *s; s++) + *s = tolower(*s); /* Change to lower case */ + } + + /* Sort by school name */ + qsort(stu, N, sizeof(Student*), cmp_school); + + /* Calculate, count and record */ + for(Student **p = stu; p < stu + N; p++) + { + switch((*p)->id[0]) /* Calculate total score */ + { + case 'B': total_score += (*p)->score / 1.5; break; + case 'A': total_score += (*p)->score; break; + case 'T': total_score += (*p)->score * 1.5; break; + } + num++; /* Count number of students */ + if(!*(p + 1) || strcmp((*p)->school, (*(p + 1))->school)) + { /* Record into an array */ + sch[M] = schools + M; + strcpy(sch[M]->name, (*p)->school); + sch[M]->score = (int)total_score; + sch[M]->num = num; + total_score = 0, num = 0, M++; /* Reset and increament counter */ + } + } + + /* Sort according to rules */ + qsort(sch, M, sizeof(School*), cmp_sort_all); + + /* Print result */ + printf("%d\n", M); + for(int i = 0, ranking = 0; i < M; i++) + { + if(i > 0 && sch[i]->score < sch[i - 1]->score) + ranking = i; + printf("%d %s %d %d\n", ranking + 1, sch[i]->name, + sch[i]->score, sch[i]->num); + } + + return 0; +} + +``` \ No newline at end of file