文章目录

  • (栈) A1051 Pop Sequence (25 分) 0.47
  • (静态链表) A1052 Linked List Sorting (25 分) 0.21
  • (静态树+先根遍历DFS) A1053 Path of Equal Weight (30 分) 0.45
  • A1054 The Dominant Color (20 分) 0.50
  • A1055 The World's Richest (25 分) 0.21
  • (队列) A1056 Mice and Rice (25 分) 0.47
  • 1057 Stack 30 2465 9368 0.26
  • A1058 A+B in Hogwarts (20 分)
  • ★(素因子分解模板) A1059 Prime Factors (25 分)
  • (简单模拟) A1061 Dating (20 分) 0.22
  • (Set) A1063 Set Similarity (25 分) 0.39
  • (完全二叉树+二叉排序树+中序遍历) A1064 Complete Binary Search Tree (30 分) 0.58
  • A1065 A+B and C (64bit) (20 分)
  • (AVL树插入+建树) A1066 Root of AVL Tree (25 分)
  • ☆☆☆(dp, 01背包) A1068 Find More Coins (30 分) 0.28
  • A1069 The Black Hole of Numbers (20 分)
  • (贪心) A1070 Mooncake (25 分)
  • A1071 Speech Patterns (25 分) 0.35
  • ★★(科学计数法) A1073 Scientific Notation (20 分)
  • (静态链表) A1074 Reversing Linked List (25 分)
  • ★★★(排序应用) A1075 PAT Judge (25 分) 0.20
  • ★(图+BFS) A1076 Forwards on Weibo (30 分) 0.38
  • A1077 Kuchiguse (20 分) 0.20
  • ★★(散列/冲突处理) A1078 Hashing (25 分)
  • (静态树+先根遍历DFS) A1079 Total Sales of Supply Chain (25 分) 0.39
  • ★★★ (复杂模拟) A1080 Graduate Admission (30 分) 0.23
  • ★(分数类) A1081 Rational Sum (20 分) 0.29
  • ☆☆☆(复杂模拟) 1082 Read Number in Chinese (25 分) 0.25
  • A1083 List Grades (25 分) 0.51
  • A1084 Broken Keyboard (20 分) 0.41
  • ★ A1085 Perfect Sequence (25 分) 0.27
  • (二叉树非递归遍历+先序中序->后序) A1086 Tree Traversals Again (25 分)
  • ★★(分数类全面总结) A1088 Rational Arithmetic (20 分) 0.30
  • ★(排序算法) A1089 Insert or Merge (25 分)
  • (静态树+DFS遍历) A1090 Highest Price in Supply Chain (25 分) 0.41
  • A1092 To Buy or Not to Buy (20 分) 0.54
  • A1093 Count PAT's (25 分)
  • (静态树+DFS) A1094 The Largest Generation (25 分) 0.41
  • ★★★(排序+复杂模拟+map应用) A1095 Cars on Campus (30 分) 0.25
  • ☆ A1096 Consecutive Factors (20 分)
  • (静态链表) A1097 Deduplication on a Linked List (25 分) 0.32
  • (建堆+堆排序+排序算法特点) A1098 Insertion or Heap Sort (25 分) 0.32
  • (静态二叉查找树+层序遍历) A1099 Build A Binary Search Tree (30 分) 0.57

(栈) A1051 Pop Sequence (25 分) 0.47

Given a stack which can keep M numbers at most. Push N numbers in the order of 1, 2, 3, …, N and pop randomly. You are supposed to tell if a given sequence of numbers is a possible pop sequence of the stack. For example, if M is 5 and N is 7, we can obtain 1, 2, 3, 4, 5, 6, 7 from the stack, but not 3, 2, 1, 7, 5, 6, 4.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains 3 numbers (all no more than 1000): M (the maximum capacity of the stack), N (the length of push sequence), and K (the number of pop sequences to be checked). Then K lines follow, each contains a pop sequence of N numbers. All the numbers in a line are separated by a space.
  • Output Specification:
    For each pop sequence, print in one line “YES” if it is indeed a possible pop sequence of the stack, or “NO” if not.
  • Sample Input:
    5 7 5
    1 2 3 4 5 6 7
    3 2 1 7 5 6 4
    7 6 5 4 3 2 1
    5 6 4 3 7 2 1
    1 7 6 5 4 3 2
    
  • Sample Output:
    YES
    NO
    NO
    YES
    NO
    

题意:给出一个容量为M的栈和一个1-N的入栈序列,要求你检查下面的K个出栈序列是否合法。用栈模拟一下就可以了。

#include <bits/stdc++.h>
using namespace std;int main() {int m, n, k, t[1010], ori[1010];scanf("%d%d%d", &m, &n, &k);while (k--) {stack<int> s;for (int i = 0; i < n; i++) scanf("%d", &t[i]); //设置出栈序列int i = 0, j = 0;for ( ; i < n; i++) {if (s.size() < m) s.push(i + 1); //依次进栈else break; //标志着栈容量已满但是无法形成该出栈序列while (!s.empty() && s.top() == t[j]) {s.pop();j++; //检查下一个元素}}if (i == j) printf("YES\n");else printf("NO\n"); //栈容量已满但是无法形成该出栈序列/此时栈非空}return 0;
}

(静态链表) A1052 Linked List Sorting (25 分) 0.21

A linked list consists of a series of structures, which are not necessarily adjacent in memory. We assume that each structure contains an integer key and a Next pointer to the next structure. Now given a linked list, you are supposed to sort the structures according to their key values in increasing order.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains a positive N (<10​5​​) and an address of the head node, where N is the total number of nodes in memory and the address of a node is a 5-digit positive integer. NULL is represented by −1.
    Then N lines follow, each describes a node in the format:

    Address Key Next
    

    where Address is the address of the node in memory, Key is an integer in [−10​5​​,10​5​​], and Next is the address of the next node. It is guaranteed that all the keys are distinct and there is no cycle in the linked list starting from the head node.

  • Output Specification:
    For each test case, the output format is the same as that of the input, where N is the total number of nodes in the list and all the nodes must be sorted order.
  • Sample Input:
    5 00001
    11111 100 -1
    00001 0 22222
    33333 100000 11111
    12345 -1 33333
    22222 1000 12345
    
  • Sample Output:
    5 12345
    12345 -1 00001
    00001 0 11111
    11111 100 22222
    22222 1000 33333
    33333 100000 -1
    

题意: 给你N个链表的结点,以及开始的地址,然后对链表进行排序,并按格式输出排序后的链表。
思路: 这道题不是很难,乙级有一道和这类似的,但是这道题有两个坑,一个是给你的元素,一部分不是链表上的,有无效结点,所以要处理一下;另外是存在空链的情况,这种情况特判输出链表结点的数量为0,首地址为-1。

一向投机取巧的方式:建立有效结点的地址表,根据地址对应的值对地址进行排序。排序完之后,直接输出即可,前一个结点的next就是后一个结点的地址,不必重新建链。

#include <bits/stdc++.h>
const int maxn = 100010;
using namespace std;
struct node {int data, next;
} List[maxn];
int myList[maxn], L = 0;
bool cmp(int a, int b) {return List[a].data < List[b].data;
}int main() {int n, firstAddr, addr;scanf("%d%d", &n, &firstAddr);for (int i = 0; i < n; i++) {scanf("%d", &addr);scanf("%d%d", &List[addr].data, &List[addr].next);}if (firstAddr == -1) { //N为正, 但是头结点地址可能为-1, 即空链printf("0 -1\n");return 0;}while (firstAddr != -1) {myList[L++] = firstAddr;firstAddr = List[firstAddr].next;}sort(myList, myList + L, cmp);myList[L++] = -1;printf("%d %05d\n", L - 1, myList[0]);for (int i = 0; i < L - 1; i++) {printf("%05d %d ", myList[i], List[myList[i]].data);if (myList[i + 1] != -1) printf("%05d\n", myList[i + 1]);else printf("-1\n");}return 0;
}

重新建链的方法可以学一学,说不定会在哪题上面用上,不过也简单,就是在遍历链表寻找有效结点的时候,不存储有效结点地址而是用vector直接存储该结点,遍历完成后,这就是一条新的有效结点链表了。

中间的方法,就是直接在遍历链表的时候标记出有效结点(像A1032 sharing一样),然后在排序的时候把有效结点排在前面。这样不用重新建链,而是改变原先的链表。节省空间一点。但这两种方法都破坏了结点地址表示的含义,不能再通过地址访问结点了,必须存储地址

#include <bits/stdc++.h>
using namespace std;
struct Node {int addr, key, next;bool flag;
} node[100000];
int cmp(Node &a, Node &b) { //有一个无效结点就把有效结点排在前面; 都是有效结点就从小到大排列return !a.flag || !b.flag ? a.flag > b.flag : a.key < b.key;
}
int main() {int n, firstAddr, cnt = 0, a, b, c;scanf("%d%d", &n, &firstAddr);for (int i = 0; i < n; i++) {scanf("%d%d%d", &a, &b, &c);node[a] = {a, b, c, false};}for (int i = firstAddr; i != -1; i = node[i].next) {node[i].flag = true; //标记有效结点cnt++;}if (cnt == 0) printf("0 -1\n");else { sort(node, node + 100000, cmp);printf("%d %05d\n", cnt, node[0].addr);for (int i = 0; i < cnt; i++) {printf("%05d %d ", node[i].addr, node[i].key);if(i != cnt - 1) printf("%05d\n", node[i + 1].addr);else printf("-1\n");}}return 0;
}

(静态树+先根遍历DFS) A1053 Path of Equal Weight (30 分) 0.45

Given a non-empty tree with root R, and with weight W​i​​ assigned to each tree node T​i​​. The weight of a path from R to L is defined to be the sum of the weights of all the nodes along the path from R to any leaf node L.

Now given any weighted tree, you are supposed to find all the paths with their weights equal to a given number. For example, let’s consider the tree showed in the following figure: for each node, the upper number is the node ID which is a two-digit number, and the lower number is the weight of that node. Suppose that the given number is 24, then there exists 4 different paths which have the same given weight: {10 5 2 7}, {10 4 10}, {10 3 3 6 2} and {10 3 3 6 2}, which correspond to the red edges in the figure.

  • Input Specification:
    Each input file contains one test case. Each case starts with a line containing 0<N≤100, the number of nodes in a tree, M (<N), the number of non-leaf nodes, and 0<S<2​30​​, the given weight number. The next line contains N positive numbers where W​i​​ (<1000) corresponds to the tree node T​i​​. 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 00.

  • Output Specification:
    For each test case, print all the paths with weight S in non-increasing order. Each path occupies a line with printed weights from the root to the leaf in order. All the numbers must be separated by a space with no extra space at the end of the line.
    Note: sequence {A​1​​,A​2​​,⋯,A​n​​} is said to be greater than sequence {B​1​​,B​2​​,⋯,B​m​​} if there exists 1≤k<min{n,m} such that A​i​​=B​i​​ for i=1,⋯,k, and A​k+1​​>B​k+1​​.
  • Sample Input:
    20 9 24
    10 2 4 3 5 10 2 18 9 7 2 2 1 3 12 1 8 6 2 2
    00 4 01 02 03 04
    02 1 05
    04 2 06 07
    03 3 11 12 13
    06 1 09
    07 2 08 10
    16 1 15
    13 3 14 16 17
    17 2 18 19
    
  • Sample Output:
    10 5 2 7
    10 4 10
    10 3 3 6 2
    10 3 3 6 2
    

注意点:

  • 这道题的点权值说是正数,实际是正整数;
  • 这里使用的是树的静态写法,用vector存储所有孩子结点的编号,考虑到最后的输出需要按照点权值序列的大小输出,因此读入时就将每个结点的子结点按照权值从大到小排列,每次遍历时就会优先遍历到权值较大的结点;
  • 下面这种写法是一种在递归中保存路径的方法使用path[maxn]数组表示路径,其中path[i]表示路径上面第i个结点的编号,然后使用初值为0的下标变量numNode,在递归中每向下一层,numNode+1,这样numNode就可以随时跟踪path[]数组当前的结点个数,便于随时加入新的结点和覆盖旧的结点;
  • 第二种写法就是使用vector,每次枚举当前访问结点的子结点的时候push_back,回溯的时候pop_back前面加入的子结点;
  • 另外,这里的DFS遍历(树的先根遍历实际上就是DFS),我的代码里面也写的不太一样,第一种是先在main函数中先访问了根结点,然后在每次枚举当前访问结点的子结点时,访问子结点,添加路径点。第二种则是普通的DFS,每次DFS函数的第一步访问当前结点。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 101;
struct Node {int weight;vector<int> child;
} node[maxn];
bool cmp(int a, int b) { //每个结点的子结点按权值从大到小排序 return node[a].weight > node[b].weight; //方便降序输出每条路径
}int n, m, s, path[maxn];
void preOrder(int root, int sum, int numNode) { if (sum > s) return; //当前点权和大于s, 直接返回 if (sum == s) { //当前和sum等于s if (node[root].child.size() != 0) return; //还没到叶子结点 for (int i = 0; i < numNode; i++) { //到达叶子结点, 输出路径 if (i > 0) printf(" ");printf("%d", node[path[i]].weight);}printf("\n");return;} for (int i = 0; i < node[root].child.size(); i++) {int child = node[root].child[i];path[numNode] = child; //记录结点到路径末尾 preOrder(child, sum + node[child].weight, numNode + 1);}
}
int main() {int id, k, t;scanf("%d%d%d", &n, &m, &s);for (int i = 0; i < n; i++) scanf("%d", &node[i].weight);for (int i = 0; i < m; i++) {scanf("%d%d", &id, &k);for (int j = 0; j < k; j++) {scanf("%d", &t);node[id].child.push_back(t); //child为结点id的孩子 } sort(node[id].child.begin(), node[id].child.end(), cmp);}path[0] = 0; //路径的第一层设置为0结点 preOrder(0, node[0].weight, 1); //DFS求解 return 0;
}
......
int n, m, s;
vector<int> pathWeight;
void preOrder(int root, int sum) { sum += node[root].weight;pathWeight.push_back(node[root].weight);if (sum > s) return; //当前点权和大于s, 直接返回 if (sum == s) { //当前和sum等于s ......} for (int i = 0; i < node[root].child.size(); i++) { preOrder(node[root].child[i], sum);pathWeight.pop_back();}
}
int main() {......preOrder(0, 0); //DFS求解 return 0;
}

A1054 The Dominant Color (20 分) 0.50

A strictly dominant color takes more than half of the total area...It is guaranteed that the strictly dominant color exists for each input image.题目中说dominant color必须超过半数,又同时保证答案必然存在,所以就等价于从一堆数字中选择出现次数最多的数,而不用判断其是否超过半数。

另外,题目如果使用普通数组做哈希,不使用map,则可能内存超限;如果使用cin读入,则可能超时。

#include <bits/stdc++.h>
using namespace std;int main() {int m, n; scanf("%d%d", &m, &n);map<int, int> mp;int t;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {scanf("%d", &t);mp[t]++;}}int domicnt = 0, domicolor = 0;for (auto it : mp) {if (it.second > domicnt) {domicolor = it.first;domicnt = it.second;}}printf("%d\n", domicolor);return 0;
}

另一种方法:摩尔碰撞法。两个不同的数字相互抵消,最后剩下的数字一定是所求数字(因为它出现超过半数)。

#include <bits/stdc++.h>
using namespace std;int main() {int m, n; scanf("%d%d", &m, &n);int k = m * n, ans, cnt, t;scanf("%d", &ans); cnt = 1;for (int i = 1; i < k; i++) {scanf("%d", &t);if (ans != t) { //两个数不同就抵消ans一次cnt--;if (cnt == 0) {ans = t; //如果cnt=0, 则选择新的数字cnt = 1;}} else cnt++; //两数相同, 记录的次数+1}printf("%d\n", ans);return 0;
}

A1055 The World’s Richest (25 分) 0.21

给出N(<=105)个人的姓名、年龄和财富,然后进行K次查询,每次查询给出年龄范围内的财富值从大到小的前M(<=100)个人的信息。
这个题目不难,但是可能的担忧是超时。先进行排序,有K(<=103)次查询,采用顺序遍历每次查询最坏的情况都是遍历整个结构体数组,因此总的复杂度为O(NlogN+K*N)。
这也是我的做法,但是全部测试点都通过了。如果超时的话,可以根据题目条件进行预处理。

#include <bits/stdc++.h>
using namespace std;
struct rich {char name[12];int age, worth;
} p[100010];
bool cmp(rich &a, rich &b) {if (a.worth != b.worth) return a.worth > b.worth; //按身价降序if (a.age != b.age) return a.age < b.age; //年龄升序int t = strcmp(a.name, b.name);if (t != 0) return t < 0; //姓名升序
}int main() {int n, k, M, amin, amax;scanf("%d%d", &n, &k);for (int i = 0; i < n; i++) scanf("%s %d %d", p[i].name, &p[i].age, &p[i].worth);sort(p, p + n, cmp);for (int i = 1; i <= k; i++) {scanf("%d%d%d", &M, &amin, &amax); printf("Case #%d:\n", i);int num = 0;for (int j = 0; j < n; j++) {if (p[j].age >= amin && p[j].age <= amax) { //在这个年龄范围printf("%s %d %d\n", p[j].name, p[j].age, p[j].worth);num++;if (num >= M) break; //最多只输出M个人}} if (num == 0) printf("None\n");}return 0;
}

(队列) A1056 Mice and Rice (25 分) 0.47

Mice and Rice is the name of a programming contest in which each programmer must write a piece of code to control the movements of a mouse in a given map. The goal of each mouse is to eat as much rice as possible in order to become a FatMouse.

First the playing order is randomly decided for N​P​​ programmers. Then every N​G​​ programmers are grouped in a match. The fattest mouse in a group wins and enters the next turn. All the losers in this turn are ranked the same. Every N​G​​ winners are then grouped in the next match until a final winner is determined.

For the sake of simplicity, assume that the weight of each mouse is fixed once the programmer submits his/her code. Given the weights of all the mice and the initial playing order, you are supposed to output the ranks for the programmers.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains 2 positive integers: N​P​​ and N​G​​ (≤1000), the number of programmers and the maximum number of mice in a group, respectively. If there are less than N​G​​ mice at the end of the player’s list, then all the mice left will be put into the last group. The second line contains N​P​​ distinct non-negative numbers W​i​​(i=0,⋯,N​P​​−1) where each W​i​​ is the weight of the i-th mouse respectively. The third line gives the initial playing order which is a permutation of 0,⋯,N​P​​−1 (assume that the programmers are numbered from 0 to N​P​​−1). All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print the final ranks in a line. The i-th number is the rank of the i-th programmer, and all the numbers must be separated by a space, with no extra space at the end of the line.
  • Sample Input:
    11 3
    25 18 0 46 37 3 19 22 57 56 10
    6 0 8 7 10 5 9 1 4 2 3
    
  • Sample Output:
    5 5 5 2 5 5 5 3 1 3 5
    

NP只老鼠的重量,给出它们的初始顺序,按照初始顺序按每NG只分为多组,最后不够NG只的也算一组。每一组选择最重的一只老鼠晋级。然后对这些晋级的老鼠再按上面的步骤,每NG只分为一组进行比较,选出最大的一批继续晋级,直到只剩下最后一只,排名为1。

  • 每次循环都会选出组数group那么多的老鼠晋级,这次循环的组数group就是下一次循环的老鼠数;
  • 每组晋级1只老鼠,当前总晋级的老鼠数目为group,且该轮未晋级的老鼠的排名一样,就是均为group+1。这样才符合样例里面的排名情况。
#include <bits/stdc++.h>
using namespace std;
struct mouse {int weight;int rank;
} mice[1010];int main() {int np, ng, group, t; scanf("%d%d", &np, &ng);for (int i = 0; i < np; i++) {scanf("%d", &mice[i].weight);}   int initOrder;queue<int> q;for (int i = 0; i < np; i++) {scanf("%d", &initOrder);q.push(initOrder); //按顺序将老鼠们的标号入队 }t = np; //temp为当前轮的比赛老鼠数目, group为组数 while (q.size() != 1) {if (t % ng == 0) group = t / ng;else group = t / ng + 1; //组数for (int i = 0; i < group; i++) {int k = q.front(); //k存放该组质量最大的老鼠的编号for (int j = 0; j < ng; j++) {if (i * ng + j >= t) break; //最后一组不足这个数目时退出 int front = q.front(); //队首老鼠编号 if (mice[front].weight > mice[k].weight) k = front; mice[front].rank = group + 1; //该轮老鼠排行group+1q.pop(); //出队这只老鼠 } q.push(k); //把胜利的这只老鼠晋级 }t = group; //group只老鼠晋级, 因此下轮总老鼠数目为group }mice[q.front()].rank = 1; //最后一只老鼠排名为1for (int i = 0; i < np; i++) {printf("%d", mice[i].rank);if (i < np - 1) printf(" ");} return 0;
}

1057 Stack 30 2465 9368 0.26

A1058 A+B in Hogwarts (20 分)

Seventeen silver Sickles to a Galleon and twenty-nine Knuts to a Sickle, it’s easy enough.” Your job is to write a program to compute A+B where A and B are given in the standard form of Galleon.Sickle.Knut (Galleon is an integer in [0,10​7​​], Sickle is an integer in [0, 17), and Knut is an integer in [0, 29)).

  • Input Specification:
    Each input file contains one test case which occupies a line with A and B in the standard form, separated by one space.
  • Output Specification:
    For each test case you should output the sum of A and B in one line, with the same format as the input.
  • Sample Input:
    3.2.1 10.16.27
    
  • Sample Output:
    14.1.28
    

看一下数据范围,Galleon最大107,全部转换为Knut就是107x17x29,往大了估算为107x20x30,大概6x109,已经溢出整型范围了。而且A和B换成Knut极端情况下都可能是6x109。因此要使用long long存储结果,但是很可能还是有一个测试点错误,原因在于计算的中途就已经溢出,所以还必须用long long存储输入的数字

#include <bits/stdc++.h>
const int Galleon = 17 * 29;
const int Sickle = 29;
int main() {long long g1, s1, k1, g2, s2, k2;scanf("%lld.%lld.%lld %lld.%lld.%lld", &g1, &s1, &k1, &g2, &s2, &k2);long long sum = (g1 + g2) * Galleon + (s1 + s2) * Sickle + k1 + k2;printf("%lld.%lld.%lld\n", sum / Galleon, sum % Galleon / Sickle, sum % Sickle);return 0;
}

★(素因子分解模板) A1059 Prime Factors (25 分)

Given any positive integer N, you are supposed to find all of its prime factors, and write them in the format N = p​1​​​k​1​​​​×p​2​​​k​2​​​​×⋯×p​m​​​k​m​​​​.

  • Input Specification:
    Each input file contains one test case which gives a positive integer N in the range of long int.
  • Output Specification:
    Factor N in the formatN = p​1​​^k​1​​*p​2​​^k​2​​*…*p​m​​^k​m​​, where p​i​​’s are prime factors of N in increasing order, and the exponent k​i​​ is the number of p​i​​ – hence when there is only one p​i​​, k​i​​ is 1 and must NOT be printed out.
  • Sample Input:
    97532468
    
  • Sample Output:
    97532468=2^2*11*17*101*1291
    

特判“1=1”的情况;一个数的素因子要么全部都小于等于sqrt(n),要么就只有一个素因子大于sqrt(n)。

#include <bits/stdc++.h>
const int maxn = 100010;
struct factor {int x, cnt;
} f[20];
int p[maxn] = {1, 1, 0}, prime[maxn], pNum = 0;
void findPrime() {for (int i = 2; i < maxn; i++) {if (p[i] == 0) {prime[pNum++] = i;for (int j = i + i; j < maxn; j += i) p[i] = 1;}}
}int main() {long long n; scanf("%lld", &n);long long k = n; //n的复制if (n == 1) printf("1=1\n"); //1不是素数, 它没有质因子 else {findPrime();//枚举素数表中根号n以内的质因子int fnum = 0, sqr = sqrt(1.0 * n); //fnum为n的不同质因子的个数for (int i = 0; i < pNum && prime[i] <= sqr; i++) {if (n % prime[i] == 0) {f[fnum].x = prime[i], f[fnum].cnt = 0;while (n % prime[i] == 0) {f[fnum].cnt++;n /= prime[i];}fnum++;}//如果n为1, 说明n的质因子全部<=sqrt(n), 及时退出if (n == 1) break; }if (n != 1) { //如果无法被根号n以内的质因子除尽 f[fnum].x = n; //那么一定有一个大于根号n的质因子 f[fnum++].cnt = 1; //即此时的n }printf("%lld=", k);for (int i = 0; i < fnum; i++) {printf("%d", f[i].x);if (f[i].cnt != 1) printf("^%d", f[i].cnt);if (i < fnum - 1) printf("*");}printf("\n");}return 0;
}

(简单模拟) A1061 Dating (20 分) 0.22

福尔摩斯的约会,这里使用了cin和cout,没有超时,因为只有4 non-empty strings of no more than 60 characters without white space in 4 lines

#include <bits/stdc++.h>
using namespace std;
char weekday[][5] = {"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};int main() {string t1, t2;cin >> t1 >> t2;int len = t1.length() < t2.length() ? t1.length() : t2.length(), i;for (i = 0; i < len; i++) {if (t1[i] == t2[i] && t1[i] >= 'A' && t1[i] <= 'G') {printf("%s ", weekday[t1[i] - 'A']);break;}}for (i += 1; i < len; i++) {if (t1[i] == t2[i] && (t1[i]>='A' && t1[i]<='N' || t1[i]>='0' && t1[i]<='9')) {printf("%02d:",  t1[i] >= 'A' ? t1[i] - 'A' + 10 : t1[i] - '0');break;}}cin >> t1 >> t2; len = t1.length() < t2.length() ? t1.length() : t2.length();for (i = 0; i < len; i++) {if (t1[i] == t2[i] && (isupper(t1[i]) || islower(t1[i]))) {printf("%02d\n", i);break;}}return 0;
}

(Set) A1063 Set Similarity (25 分) 0.39

CodeUP做过的题目。只要会熟练地使用set就行了。

#include <bits/stdc++.h>
using namespace std;int main() {int n, cnt, t, k, s1, s2;scanf("%d", &n);set<int> sets[n + 1];for (int i = 1; i <= n; i++) {scanf("%d", &cnt); while (cnt--) {scanf("%d", &t);sets[i].insert(t);}}scanf("%d", &k);while (k--) {int nc = 0, nt = 0; scanf("%d%d", &s1, &s2);for (auto a : sets[s1]) if (sets[s2].find(a) != sets[s2].end()) nc++; //交集元素数目+1nt = sets[s1].size() + sets[s2].size() - nc; printf("%.1lf%%\n", nc * 100.0 / nt);}return 0;
}

(完全二叉树+二叉排序树+中序遍历) A1064 Complete Binary Search Tree (30 分) 0.58

A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties:

The left subtree of a node contains only nodes with keys less than the node’s key.
The right subtree of a node contains only nodes with keys greater than or equal to the node’s key.
Both the left and right subtrees must also be binary search trees.

A Complete Binary Tree (CBT) is a tree that is completely filled, with the possible exception of the bottom level, which is filled from left to right.
Now given a sequence of distinct non-negative integer keys, a unique BST can be constructed if it is required that the tree must also be a CBT. You are supposed to output the level order traversal sequence of this BST.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains a positive integer N (≤1000). Then N distinct non-negative integer keys are given in the next line. All the numbers in a line are separated by a space and are no greater than 2000.
  • Output Specification:
    For each test case, print in one line the level order traversal sequence of the corresponding complete binary search tree. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.
  • Sample Input:
    10
    1 2 3 4 5 6 7 8 9 0
    
  • Sample Output:
    6 3 8 1 5 7 9 0 2 4
    

题意:给出N个非负整数,要用它们构建一棵完全二叉排序树,输出这棵完全二叉排序树的层序遍历序列。
思路:

  • 利用完全二叉树的结构性质,用一个数组cbt代表完全二叉树的结果,根结点从1开始;最后得到的完全二叉排序树数组本身就是层序
  • 二叉排序树其中序遍历序列是递增的,那么先将输入的数字从小到大排序,然后对cbt数组表示的二叉树进行中序遍历,将数字从小到大依次填入数组,最后得到一棵完全二叉排序树。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;  //cbt以完全二叉树的数组形式存储
int n, number[maxn], cbt[maxn], k = 0;
void inorder(int root) { //中序遍历填充形成完全二叉排序树数组if(root <= n) { inorder(root * 2); //往左子树递归cbt[root] = number[k++]; //根结点赋值inorder(root * 2 + 1); //往右子树递归}
}
int main() {scanf("%d", &n);for (int i = 0; i < n; i++) scanf("%d", &number[i]);  sort(number, number + n); //从小到大排序, 即为二叉搜索树的中序序列inorder(1);  //cbt根结点从1开始for(int i = 1; i <= n; i++) { if (i > 1) printf(" "); printf("%d", cbt[i]); //cbt本身就是层序}return 0;
}

A1065 A+B and C (64bit) (20 分)

Given three integers A, B and C in [−2​63​​,2​63​​], you are supposed to tell whether A+B>C.

  • Sample Input:
3
1 2 3
2 3 4
9223372036854775807 -9223372036854775808 0
  • Sample Output:
Case #1: false
Case #2: true
Case #3: false

这题我想到的有大整数类,负数相加用减法。但是有点麻烦。如果看清了题目条件的话,会有更简单的方法。

#include <bits/stdc++.h>int main() {int n;scanf("%d", &n);for (int i = 1; i <= n; i++) {long long a, b, c, add;scanf("%lld%lld%lld", &a, &b, &c);add = a + b;int flag; if (a > 0 && b > 0 && add < 0) flag = 1; //正溢出else if (a < 0 && b < 0 && add >= 0) flag = 0; //负溢出else if (add > c) flag = 1;else flag = 0;if (flag) printf("Case #%d: true\n", i);else printf("Case #%d: false\n", i);}return 0;
}

(AVL树插入+建树) A1066 Root of AVL Tree (25 分)

An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property. Figures 1-4 illustrate the rotation rules.


Now given a sequence of insertions, you are supposed to tell the root of the resulting AVL tree.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains a positive integer N (≤20) which is the total number of keys to be inserted. Then N distinct integer keys are given in the next line. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print the root of the resulting AVL tree in one line.
  • Sample Input 1:
    5
    88 70 61 96 120
    
  • Sample Output 1:
    70
    
  • Sample Input 2:
    7
    88 70 61 96 120 90 65
    
  • Sample Output 2:
    88
    

题意:给出一个distinct的整数序列,要求按顺序插入到一棵AVL树中,最后输出根结点的数据。
思路:建一棵AVL树,使用旋转来进行平衡。

#include <bits/stdc++.h>
using namespace std;
struct node {int data, height; //高度 node *left, *right;
};
node* newNode(int v) {node* root = new node;root->data = v;root->height = 1; //结点高度初始化为1 root->left = root->right = NULL;return root;
}
int getHeight(node *root) {if (root == NULL) return 0; //空结点高度为0 else return root->height;
}
int getFactor(node *root) { //左子树高度-右子树高度return getHeight(root->left) - getHeight(root->right);
}
void updateHeight(node *root) { //结点的高度为其左右子树中高度更高的那个+1root->height = max(getHeight(root->left), getHeight(root->right)) + 1;
}
void L(node *&root) { //左旋, 左旋拎右左挂右 node *temp = root->right;root->right = temp->left;temp->left = root;updateHeight(root), updateHeight(temp); //更新结点高度  root = temp; //设定为根结点
}
void R(node *&root) { //右旋, 右旋拎左右挂左 node *temp = root->left;root->left = temp->right;temp->right = root; updateHeight(root), updateHeight(temp); //更新结点高度  root = temp; //设定为根结点
}
void insert(node *&root, int v) {if (root == NULL) {root = newNode(v);return;} if (v < root->data) { insert(root->left, v); //往左子树插入 updateHeight(root); //插入后回溯更新树高 int nf = getFactor(root);if (nf > 1) {int bf = getFactor(root->left);  if (bf > 0) R(root); //LL型else { L(root->left); R(root); } //LR型  } } else {insert(root->right, v); //往右子树插入updateHeight(root); //插入后回溯更新树高 int nf = getFactor(root);if (nf < -1) {int bf = getFactor(root->right);  if (bf < 0) L(root); //RR型else { R(root->right); L(root); } //RL型  }        }
}int main() {int n, t;scanf("%d", &n);node *root = NULL; //新建空根结点 for (int i = 0; i < n; i++) {scanf("%d", &t); insert(root, t);} printf("%d\n", root->data);return 0;
}

☆☆☆(dp, 01背包) A1068 Find More Coins (30 分) 0.28

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
const int maxv = 110;
int w[maxn], dp[maxv] = {0}; //w[i]为钱币的价值
bool choice[maxn][maxv], flag[maxn];
bool cmp(int a, int b) {return a > b;
}
int main() {int n, m;scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++) scanf("%d", &w[i]);sort(w + 1, w + n + 1, cmp);for (int i = 1; i <= n; i++) {for (int v = m; v >= w[i]; v--) {if (dp[v] <= dp[v - w[i]] + w[i]) {dp[v] = dp[v - w[i]] + w[i];choice[i][v] = 1; //放入第i件物品} else choice[i][v] = 0;}}if (dp[m] != m) printf("No Solution"); //无解else {//记录最优路径int k = n, num = 0, v = m;while (k >= 0) {if (choice[k][v] == 1) {flag[k] = true;v -= w[k];num++;} else flag[k] = false;k--;}//输出方案for (int i = n; i >= 1; i--) {if (flag[i] == true) {printf("%d", w[i]);num--;if (num > 0)  printf(" ");}}}return 0;
}

A1069 The Black Hole of Numbers (20 分)

For any 4-digit integer except the ones with all the digits being the same, if we sort the digits in non-increasing order first, and then in non-decreasing order, a new number can be obtained by taking the second number from the first one. Repeat in this manner we will soon end up at the number 6174 – the black hole of 4-digit numbers. This number is named Kaprekar Constant.

For example, start from 6767, we’ll get:

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

Given any 4-digit number, you are supposed to illustrate the way it gets into the black hole.

  • Input Specification:
    Each input file contains one test case which gives a positive integer N in the range (0,10​4​​).
  • Output Specification:
    If all the 4 digits of N are the same, print in one line the equation N - N = 0000. Else print each step of calculation in a line until 6174 comes out as the difference. All the numbers must be printed as 4-digit numbers.
  • Sample Input 1:
    6767
    
  • Sample Output 1:
    7766 - 6677 = 1089
    9810 - 0189 = 9621
    9621 - 1269 = 8352
    8532 - 2358 = 6174
    
  • Sample Input 2:
    2222
    
  • Sample Output 2:
    2222 - 2222 = 0000
    

这题不难,但是有个测试点可能过不了。题目中说,除了4位数字全部相等的要特殊输出外,其他所有数字都要按照题目要求先递减排序,再递增排序,用最大值减去最小值,直到出现6174。因此,如果直接输入6174,我们的程序需要输出7641 - 1467 = 6174,而非直接退出。

#include <bits/stdc++.h>
using namespace std;
bool cmp(int a, int b) { return a > b;   }void intToArray(int n, int a[]) {for (int i = 0; i < 4; i++) {a[i] = n % 10;n /= 10;}
}
int arrayToInt(int a[]) {int sum = 0;for (int i = 0; i < 4; i++) sum = sum * 10 + a[i];    return sum;
}int main() {int n, num[4]; scanf("%d", &n);while (1) {intToArray(n, num);sort(num, num + 4); //从小到大排序 int min = arrayToInt(num);sort(num, num + 4, cmp); //从大到小排序 int max = arrayToInt(num);n = max - min;printf("%04d - %04d = %04d\n", max, min, n);if (n == 0 || n == 6174) break;    }return 0;
}

(贪心) A1070 Mooncake (25 分)

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains 2 positive integers N (≤1000), the number of different kinds of mooncakes, and D (≤500 thousand tons), the maximum total demand of the market. Then the second line gives the positive inventory amounts (in thousand tons), and the third line gives the positive prices (in billion yuans) of N kinds of mooncakes. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print the maximum profit (in billion yuans) in one line, accurate up to 2 decimal places.

每次选择单价更高的月饼卖出以获得最大利润。注意题目的陷阱。这里只说月饼种类和市场总需求为正整数,而月饼总售价和月饼库存量都只是正数,要用double存储。同时,市场需求减少一种月饼的库存量后不一定是整数,为了计算的正确性,市场总需求也要存为浮点数。另外,存在市场需求超过市场供给的时候,无法让市场需求全部满足,因此不能用D==0作为结束条件。只要获得最大利润就可以了。

#include <bits/stdc++.h>
using namespace std;
struct mooncake {double amount, price;double single; //单价
} cakes[1010];bool cmp(mooncake m1, mooncake m2) {return m1.single > m2.single;
}int main() {int n;double D;scanf("%d%lf", &n, &D);for (int i = 0; i < n; i++) scanf("%lf", &cakes[i].amount);for (int i = 0; i < n; i++) {scanf("%lf", &cakes[i].price);cakes[i].single = cakes[i].price / cakes[i].amount;}sort(cakes, cakes + n, cmp);double maxProfit = 0.0;for (int i = 0; i < n; i++) {if (cakes[i].amount >= D) {maxProfit += D * cakes[i].single;D = 0; break;} else {maxProfit += cakes[i].price;D -= cakes[i].amount;}} printf("%.2lf\n", maxProfit);return 0;
}

A1071 Speech Patterns (25 分) 0.35

People often have a preference among synonyms of the same word. For example, some may prefer “the police”, while others may prefer “the cops”. Analyzing such patterns can help to narrow down a speaker’s identity, which is useful when validating, for example, whether it’s still the same person behind an online avatar.

Now given a paragraph of text sampled from someone’s speech, can you find the person’s most commonly used word?

  • Input Specification:
    Each input file contains one test case. For each case, there is one line of text no more than 1048576 characters in length, terminated by a carriage return \n. The input contains at least one alphanumerical character, i.e., one character from the set [0-9 A-Z a-z].
  • Output Specification:
    For each test case, print in one line the most commonly occurring word in the input text, followed by a space and the number of times it has occurred in the input. If there are more than one such words, print the lexicographically smallest one. The word should be printed in all lower case. Here a “word” is defined as a continuous sequence of alphanumerical characters separated by non-alphanumerical characters or the line beginning/end.
    Note that words are case insensitive.
  • Sample Input:
    Can1: "Can a can can a can?  It can!"
    
  • Sample Output:
    can 5
    

这道题最麻烦的就在于如何从一个字符串中分割出词来,这些词被非字母数字的字符分割开来,没有太多规律。这种题目可以事先多想几个测试点。使用stl的string和map会省很多心。

#include <bits/stdc++.h>
using namespace std;int main() {string s, t;map<string, int> words;getline(cin, s);for (int i = 0; i < s.size(); i++) { if (isalnum(s[i])) {t += isupper(s[i]) ? tolower(s[i]) : s[i];} else if (t.size() != 0) {words[t]++;t = "";}} //否则123 123 1 123这样的就无法通过,最后一个测试点就是这个,会扣两分if (t != "") words[t]++; int max = 0;for (auto k : words) {if (k.second > max) { //map内部自动按键值的字典序排序max = k.second;t = k.first;}}cout << t << " " << max << endl;return 0;
}

★★(科学计数法) A1073 Scientific Notation (20 分)

Scientific notation is the way that scientists easily handle very large numbers or very small numbers. The notation matches the regular expression[+-][1-9].[0-9]+E[+-][0-9]+·…

乙级做过的题目。总之,先处理E的位置,然后看指数大小

#include <bits/stdc++.h>
using namespace std;int main() {string sci;cin >> sci;if (sci[0] == '-') { //处理负号 printf("-");} int epos = 0; //E的位置 while (sci[epos] != 'E') epos++;int exp = 0;for (int i = epos + 2; i < sci.length(); i++) {exp = exp * 10 + (sci[i] - '0');}if (exp == 0) { //特判指数为0for (int i = 1; i < epos; i++) {printf("%c", sci[i]);}} else {if (sci[epos + 1] == '-') { /* 指数为负 */ printf("0."); //输出小数点和连续的0 for (int i = 0; i < exp - 1; i++) printf("0");printf("%c", sci[1]); //输出除了小数以外的数字 for (int i = 3; i < epos; i++) {printf("%c", sci[i]); } } else { //指数为正 //输出小数点移动之后的数字 for (int i = 1; i < epos; i++) {if (sci[i] == '.') continue; //略过原小数点 printf("%c", sci[i]); //输出当前数位 if (i == exp + 2 && epos - 3 != exp) printf("."); } //指数太大, 输出多余的0 for (int i = 0; i < exp - (epos - 3); i++)  printf("0");}}return 0;
}

(静态链表) A1074 Reversing Linked List (25 分)

Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K=3, then you must output 3→2→1→6→5→4; if K=4, you must output 4→3→2→1→5→6.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains the address of the first node, a positive N (≤10​5​​) which is the total number of nodes, and a positive K (≤N) which is the length of the sublist to be reversed. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.

    Then N lines follow, each describes a node in the format:

    Address Data Next
    

    where Address is the position of the node, Data is an integer, and Next is the position of the next node.

  • Output Specification:
    For each case, output the resulting ordered linked list. Each node occupies a line, and is printed in the same format as in the input.

  • Sample Input:

    00100 6 4
    00000 4 99999
    00100 1 12309
    68237 6 -1
    33218 3 00000
    99999 5 68237
    12309 2 33218
    
  • Sample Output:

    00000 4 33218
    33218 3 12309
    12309 2 00100
    00100 1 99999
    99999 5 68237
    68237 6 -1
    

乙级做过的题目。使用静态链表,其原理是哈希,对于小于5位数的地址来说很好用。通过建立一个结构体数组,并令数组下标直接作为结点地址,来达到直接访问数组中的元素就能访问结点的效果。另外,由于结点的访问非常方便,因此静态链表不需要头结点,但要有头指针(即第一个元素的地址)。
关键的一点在于要通过题目给出的链表有效地址firstAddr遍历整条链表,找出并记录有效结点的地址和数量,每k个结点进行反转。无效结点不输出,另外,-1的输出必须小心。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct node {int addr, data, next;
} List[maxn];
int addrList[maxn], L = 0; //记录存在链表中的元素个数int main() {int firstAddr, n, k, addr;scanf("%d%d%d", &firstAddr, &n, &k);for (int i = 0; i < n; i++) {cin >> addr;cin >> List[addr].data >> List[addr].next;}while (firstAddr != -1) {addrList[L++] = firstAddr;firstAddr = List[firstAddr].next;}int step = L / k; //反转几段addrList[L++] = -1; //链表结尾 for (int i = 0; i < step; i++) { //逆转每一段的地址int begin = i * k, end = (i + 1) * k;reverse(addrList + begin, addrList + end);}for (int i = 0; i < L - 1; i++) {printf("%05d %d ", addrList[i], List[addrList[i]].data);if (addrList[i + 1] == -1) printf("-1\n");else printf("%05d\n", addrList[i + 1]);}return 0;
}

★★★(排序应用) A1075 PAT Judge (25 分) 0.20

The ranklist of PAT is generated from the status list, which shows the scores of the submissions. This time you are supposed to generate the ranklist for PAT.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains 3 positive integers, N (≤10​4​​), the total number of users, K (≤5), the total number of problems, and M (≤10​5​​), the total number of submissions. It is then assumed that the user id’s are 5-digit numbers from 00001 to N, and the problem id’s are from 1 to K. The next line contains K positive integers p[i] (i=1, …, K), where p[i] corresponds to the full mark of the i-th problem. Then M lines follow, each gives the information of a submission in the following format:

    user_id problem_id partial_score_obtained
    

    where partial_score_obtained is either −1 if the submission cannot even pass the compiler, or is an integer in the range [0, p[problem_id]]. All the numbers in a line are separated by a space.

  • Output Specification:
    For each test case, you are supposed to output the ranklist in the following format:

    rank user_id total_score s[1] ... s[K]
    

    where rank is calculated according to the total_score, and all the users with the same total_score obtain the same rank; and s[i] is the partial score obtained for the i-th problem. If a user has never submitted a solution for a problem, then “-” must be printed at the corresponding position. If a user has submitted several solutions to solve one problem, then the highest score will be counted.

    The ranklist must be printed in non-decreasing order of the ranks. For those who have the same rank, users must be sorted in nonincreasing order according to the number of perfectly solved problems. And if there is still a tie, then they must be printed in increasing order of their id’s. For those who has never submitted any solution that can pass the compiler, or has never submitted any solution, they must NOT be shown on the ranklist. It is guaranteed that at least one user can be shown on the ranklist.

  • Sample Input:

    7 4 20
    20 25 25 30
    00002 2 12
    00007 4 17
    00005 1 19
    00007 2 25
    00005 1 20
    00002 2 2
    00005 1 15
    00001 1 18
    00004 3 25
    00002 2 25
    00005 3 22
    00006 4 -1
    00001 2 18
    00002 1 20
    00004 1 15
    00002 4 18
    00001 3 4
    00001 4 2
    00005 2 -1
    00004 2 0
    
  • Sample Output:

    1 00002 63 20 25 - 18
    2 00005 42 20 0 22 -
    2 00007 42 - 25 - 17
    2 00001 42 18 18 4 2
    5 00004 40 15 0 25 -
    

对于这种涉及到了排序、排名和复杂要求的问题,要在第一时间理顺逻辑。这道题以后还会多做几次的。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
struct person {int id;     //准考证号int grade;  //总分int solved; //完美解题数bool pass;  //是否有通过编译的提交, 需要输出的标记 int s[6];   //每道题的得分person() {solved = 0;   //完美解题数初始为0pass = false; //初始化为没有能通过编译的提交grade = 0; memset(s, -1, sizeof(s)); //题目得分记为-1}
} p[maxn];
bool cmp(person &a, person &b) {if (a.grade != b.grade) return a.grade > b.grade; //按总分降序 if (a.solved != b.solved) return a.solved > b.solved; //按照完美解决的问题数降序if (a.id != b.id) return a.id < b.id; //按照id升序
}
int pscore[6] = {0}; //1-5每道题的满分
int main() {int n, k, m, id, pid, part; scanf("%d%d%d", &n, &k, &m);for (int i = 1; i <= n; i++) {p[i].id = i;} for (int i = 1; i <= k; i++) scanf("%d", &pscore[i]); //每个问题的总分 for (int i = 0; i < m; i++) {scanf("%d%d%d", &id, &pid, &part);p[id].id = id;if (part != -1) //有通过编译的提交, 需要输出 p[id].pass = 1;if (part == -1 && p[id].s[pid] == -1) { //某题第一次编译错误p[id].s[pid] = 0; //记该题为0分, 便于输出 } if (part == pscore[pid] && p[id].s[pid] != pscore[pid]) p[id].solved++; //完美解决的问题数 if (p[id].s[pid] < part)   p[id].s[pid] = part; //所有提交中最高的分数}for (int i = 1; i <= n; i++) {for (int j = 1; j <= k; j++) {if (p[i].s[j] != -1) p[i].grade += p[i].s[j];}}sort(p + 1, p + n + 1, cmp);int r = 1; //当前排名for (int i = 1; i <= n && p[i].pass; i++) { //提交了通过编译的解答, 就要输出该考生信息if (i >0 && p[i].grade != p[i - 1].grade) {r = i; //当前考生分数低于前面一位考生, 排名为在该考生之前的总考生数}printf("%d %05d %d", r, p[i].id, p[i].grade); //打印排名和idfor (int j = 1; j <= k; j++) {if (p[i].s[j] != -1) printf(" %d", p[i].s[j]);else printf(" -");}printf("\n");}return 0;
}

★(图+BFS) A1076 Forwards on Weibo (30 分) 0.38

Weibo is known as the Chinese version of Twitter. One user on Weibo may have many followers, and may follow many other users as well. Hence a social network is formed with followers relations. When a user makes a post on Weibo, all his/her followers can view and forward his/her post, which can then be forwarded again by their followers. Now given a social network, you are supposed to calculate the maximum potential amount of forwards for any specific user, assuming that only L levels of indirect followers are counted.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains 2 positive integers: N (≤1000), the number of users; and L (≤6), the number of levels of indirect followers that are counted. Hence it is assumed that all the users are numbered from 1 to N. Then N lines follow, each in the format:

    M[i] user_list[i]
    

    where M[i] (≤100) is the total number of people that user[i] follows; and user_list[i] is a list of the M[i] users that followed by user[i]. It is guaranteed that no one can follow oneself. All the numbers are separated by a space.
    Then finally a positive K is given, followed by K UserID’s for query.

  • Output Specification:
    For each UserID, you are supposed to print in one line the maximum potential amount of forwards this user can trigger, assuming that everyone who can view the initial post will forward it once, and that only L levels of indirect followers are counted.
  • Sample Input:
    7 3
    3 2 3 4
    0
    2 5 6
    2 3 1
    2 3 4
    1 4
    1 5
    2 2 6
    
  • Sample Output:
    4
    5
    

题意:每个微博用户可能被若干个其他用户关注,该用户发布一条消息时,他的关注者就会看到这条消息并转发它一次且仅一次(该消息的最初发布者不会转发自己的消息),且这条消息还会被转发者的关注者再次转发。现在给定N个用户的关注情况,以及转发层数上限,询问消息最多会被转发多少次

这就是一个BFS+数层号的题目。不过建图的时候要小心,题目中给出的数据是用户关注的情况,而非我们需要的被关注的情况。所以如果用户A关注了用户B,需要建立由用户B指向用户A的有向边。另外,用户的结点是从1开始编号的。

#include <bits/stdc++.h>
using namespace std;
const int maxv = 1010;
int N, L; //1-N
bool inq[maxv] = {false};
//逆邻接表, 表示一种被关注的有向关系
struct node {int v, layer;node(int _v, int _l) : v(_v), layer(_l) { }
};
vector<node> Adj[maxv]; void BFS(int u, int &forw) {fill(inq, inq + maxv, 0); queue<node> q;q.push(node(u, 0));inq[u] = true;while (!q.empty()) {node nowVisit = q.front(); q.pop();if (nowVisit.layer > 0 && nowVisit.layer <= L) forw++;else if (nowVisit.layer > L) return;int f = nowVisit.v;for (int v = 0; v < Adj[f].size(); v++) {node next = Adj[f][v];next.layer = nowVisit.layer + 1; //next层号等于上一层+1 if (inq[next.v] == false) {q.push(next);inq[next.v] = true;}} }}void BFSTrave() {int num, query; scanf("%d", &num);for (int i = 0; i < num; i++) {scanf("%d", &query);int forwards = 0;BFS(query, forwards);printf("%d\n", forwards);}
}int main() {scanf("%d%d", &N, &L);int follows_num, temp;for (int i = 1; i <= N; i++) {scanf("%d", &follows_num); //编号为i的人follow的明星数目while (follows_num--) {scanf("%d", &temp);Adj[temp].push_back(node(i, 0)); //编号为temp的明星被followed } }BFSTrave(); return 0;
}

A1077 Kuchiguse (20 分) 0.20

这个题是求多个字符串的最长公共后缀,通过将所有字符串反转,可以大大减少工作量。

#include <bits/stdc++.h>
using namespace std;int main() {int n, minLen = 257; scanf("%d\n", &n);string s[n];for (int i = 0; i < n; i++) {getline(cin, s[i]); //以换行符作为截断点 reverse(s[i].begin(), s[i].end());if (s[i].length() < minLen) minLen = s[i].length();}int ans = 0; for (int i = 0; i < minLen; i++) { //判断所有字符的第i个字符是否相等 char t = s[0][i]; //取第一个字符串的第i个字符bool same = true; for (int j = 1; j < n; j++) { //判断其余字符串的第i个字符是否等于t if (t != s[j][i]) { //一个不等就停止枚举 same = false; break; //公共前缀到此为止 }}if (same) ans++; //若所有字符的第i位相等, 公共前缀长度加1 else break;}if (ans) {for (int i = ans - 1; i >= 0; i--) {printf("%c", s[0][i]);}} else {printf("nai"); //不存在公共前缀 }return 0;
}

★★(散列/冲突处理) A1078 Hashing (25 分)

The task of this problem is simple: insert a sequence of distinct positive integers into a hash table, and output the positions of the input numbers. The hash function is defined to be H(key)=key%TSize where TSize is the maximum size of the hash table. Quadratic probing (with positive increments only) is used to solve the collisions.

Note that the table size is better to be prime. If the maximum size given by the user is not prime, you must re-define the table size to be the smallest prime number which is larger than the size given by the user.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains two positive numbers: MSize (≤10​4​​) and N (≤MSize) which are the user-defined table size and the number of input numbers, respectively. Then N distinct positive integers are given in the next line. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print the corresponding positions (index starts from 0) of the input numbers in one line. All the numbers in a line are separated by a space, and there must be no extra space at the end of the line. In case it is impossible to insert the number, print “-” instead.
  • Sample Input:
    4 4
    10 6 4 15
    
  • Sample Output:
    0 1 4 -
    

题意:给出散列表长和欲插入的元素,按照读入的顺序插入散列表中,可以插入则输出插入位置,不然就输出”-“。另外,如果表长不是素数,还要选择第一个比它大的素数。

其中,散列函数为H(key)=key%TSize,冲突解决方法为只往正向增加的二次探测法,初始隐含step=0,即H(a),发生冲突后,让a按照a+12,a+22,a+32……的顺序调整a的值,即step从1增长到TSize-1,如果step达到TSize时还没有找到可用位置,表明无法插入这个元素。总的来说,冲突处理公式为M = (a + step * step) % TSize

#include <bits/stdc++.h>
bool isPrime(int n) { //判断n是否为素数 if (n <= 1) return false;int sqr = (int)sqrt(1.0 * n);for (int i = 2; i <= sqr; i++) if (n % i == 0) return false;return true;
}
const int maxn = 11111;
bool hashTable[maxn] = {false}; //hashTable[x] == fasle则x号位没有被使用
int msize;
int hashFunc(int n) { int M = n % msize;if (hashTable[M] == false) {hashTable[M] = true;return M;} else { //正向二次探测法int step; //二次方探查法步长for (step = 1; step < msize; step++) { //可以证明msize为循环节 M = (n + step * step) % msize; //冲突处理公式if (hashTable[M] == false) {hashTable[M] = true;return M;}} //step如果自增到msize时还没有找到可用位置, 则表明这个元素无法插入if (step >= msize) return -1;}
}int main() {int n, num, t; scanf("%d%d", &msize, &n);while (isPrime(msize) == false) { //找到第一个比MSize大的素数msize++;}for (int j = 0; j < n; j++) {scanf("%d", &num);t = hashFunc(num);if (j > 0) printf(" ");if (t != -1) printf("%d", t);else printf("-");}return 0;
}

(静态树+先根遍历DFS) A1079 Total Sales of Supply Chain (25 分) 0.39

A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone involved in moving a product from supplier to customer.

Starting from one root supplier, everyone on the chain buys products from one’s supplier in a price P and sell or distribute them in a price that is r% higher than P. Only the retailers will face the customers. It is assumed that each member in the supply chain has exactly one supplier except the root supplier, and there is no supply cycle.

Now given a supply chain, you are supposed to tell the total sales from all the retailers.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains three positive numbers: N (≤10​5​​), the total number of the members in the supply chain (and hence their ID’s are numbered from 0 to N−1, and the root supplier’s ID is 0); P, the unit price given by the root supplier; and r, the percentage rate of price increment for each distributor or retailer. Then N lines follow, each describes a distributor or retailer in the following format:

    K​i​​ ID[1] ID[2] ... ID[K​i​​]
    

    where in the i-th line, K​i​​ is the total number of distributors or retailers who receive products from supplier i, and is then followed by the ID’s of these distributors or retailers. K​j​​ being 0 means that the j-th member is a retailer, then instead the total amount of the product will be given after K​j​​. All the numbers in a line are separated by a space.

  • Output Specification:
    For each test case, print in one line the total sales we can expect from all the retailers, accurate up to 1 decimal place. It is guaranteed that the number will not exceed 10​10​​.
  • Sample Input:
    10 1.80 1.00
    3 2 3 5
    1 9
    1 4
    1 7
    0 7
    2 6 1
    1 8
    0 9
    0 4
    0 3
    
  • Sample Output:
    42.4
    

题意:给出一棵供应、分销、销售的树,树根处的价格为P,每往下一层,该层货物价格会增加r%,给出每个叶子结点的货物量,求出价格之和。

  • 货物量使用double类型,因为题目没有说会是整数;
  • 用DFS,递归到结点root的子结点个数为0时,表示到达了叶子结点,让ans加上該结点的货物量和此时的价格;
  • 类似的题目有A1090。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct Node {double pamount;vector<int> child;
} node[maxn];
int n;
double p, r, ans = 0.0;
void DFS(int root, double price) {if (node[root].child.size() == 0) { ans += node[root].pamount * price;return;} else {for (int i = 0; i < node[root].child.size(); i++) DFS(node[root].child[i], price * (1.0 + r));}
}int main() {int k, t;scanf("%d%lf%lf", &n, &p, &r);r /= 100.0;for (int i = 0; i < n; i++) {scanf("%d", &k);if (k == 0) scanf("%lf", &node[i].pamount); else {for (int j = 0; j < k; j++) {scanf("%d", &t);node[i].child.push_back(t);}  }}DFS(0, p);printf("%.1lf\n", ans);return 0;
}

★★★ (复杂模拟) A1080 Graduate Admission (30 分) 0.23

题目大意:每个考生有两个成绩:GE和GI,最终成绩为(GE + GI) / 2;按照最终成绩排名,如果最终成绩相同,就按照GE排名,如果仍然相同,他们的排名就是相同的。每个申请者有K个选择院校,每个学校也有招生人数限制。按照排名先后,如果当前考生的第一个志愿学校的名额还没满,就录取进去;如果当前志愿名额满了但是该校最后一个录取的人的排名和当前考生相同,则不管招生人数限制,依旧应该被录取;否则考虑该生的下一个志愿。如果所有志愿都没有能被录取,则该生落榜。

分析:

  1. stu容器里放学生{id, ge, gi, fin, choice(容器里放学生报考学校的id)}, schQuota数组放招生计划的数量,nowQuota数组存放当前学校已经招收的学生数admits数组里放的容器,容器里是学校已经招的学生的id
  2. 对学生按照分数排序,依次学生遍历,分数最高的学生先挑学校;
  3. 对于每个学生录取到哪里:依次遍历学生的报考志愿,如果(没招满 || 他与已经招的学生的最后一名成绩并列)就把他招进去,该学生录取结果即可确定,更新该学校已经招生的人数,并把该学生加入该学校录取容器中;
  4. 输出学校录取情况时学生id顺序是乱的,要先从小到大排序,然后输出。每个学校占一行;
  5. 排序函数要用&引用传参,不然会超时;
  6. 因为分数fg = ge + gi 不会超出int,fg / 2 和fg排名效果一样, 不除2不会影响结果,而且还可以巧妙躲避除2后double不能精确表示的问题。
#include <bits/stdc++.h>
using namespace std;
struct student {int id, rank, ge, gi, fg; //最终成绩 vector<int> choice;
};
bool cmp(student &a, student &b) {if (a.fg != b.fg) return a.fg > b.fg;else return a.ge > b.ge;
}
bool cmp2(student &a, student &b) {return a.id < b.id;
}
int main() {int n, m, k; //总申请数,学校数,每个学生的择校数scanf("%d%d%d", &n, &m, &k);int schQuota[110], nowQuota[110] = {0}; //记录每个学校的最大录取数 vector<student> stu(n + 1), admits[110];for (int i = 0; i < m; i++) scanf("%d", &schQuota[i]);for (int i = 0; i < n; i++) {stu[i].id = i;scanf("%d%d", &stu[i].ge, &stu[i].gi);stu[i].fg = stu[i].ge + stu[i].gi;stu[i].choice.resize(k);for (int j = 0; j < k; j++) {scanf("%d", &stu[i].choice[j]);}}sort(stu.begin(), stu.end(), cmp);for (int i = 0; i < n; i++) {for (int j = 0; j < k; j++) {int t = stu[i].choice[j];int lastIndex = nowQuota[t] - 1; //如果当前志愿学校的已招生人数已满, 但该校最后录取的考生与其排名相同if (nowQuota[t] < schQuota[t] || (stu[i].fg == admits[t][lastIndex].fg && stu[i].ge == admits[t][lastIndex].ge)) {admits[t].push_back(stu[i]); //插入ID  nowQuota[t]++; break;}}}for (int i = 0; i < m; i++) {sort(admits[i].begin(), admits[i].end(), cmp2);for(int j = 0; j < nowQuota[i]; j++) {if(j != 0) printf(" ");printf("%d", admits[i][j].id);}printf("\n");}return 0;
}

★(分数类) A1081 Rational Sum (20 分) 0.29

Given N rational numbers in the form numerator/denominator, you are supposed to calculate their sum.

  • Input Specification:
    Each input file contains one test case. Each case starts with a positive integer N (≤100), followed in the next line N rational numbers a1/b1 a2/b2 … where all the numerators and denominators are in the range of long int. If there is a negative number, then the sign must appear in front of the numerator.
  • Output Specification:
    For each test case, output the sum in the simplest form integer numerator/denominator where integer is the integer part of the sum, numerator < denominator, and the numerator and the denominator have no common factor. You must output only the fractional part if the integer part is 0.
  • Sample Input 1:
    5
    2/5 4/15 1/30 -2/60 8/3
    
  • Sample Output 1:
    3 1/3
    
  • Sample Input 2:
    2
    4/3 2/3
    
  • Sample Output 2:
    2
    
  • Sample Input 3:
    3
    1/3 -1/6 1/8
    
  • Sample Output 3:
    7/24
    

题意:给定n个分数,求分数的和,分数前面可能有负号。就是分数类的这一套模板,要熟练掌握。

  • 注意点:负数无需特殊处理,只需要当作分子为负数的分数即可;数据范围为int,那么在两个分母相乘等情况下可能溢出,因此一定要使用long long;必须在每一步加法后进行约分,如果全部加完后再约分,也会溢出;要注意计算的是分子分母绝对值的公约数
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll gcd(ll a, ll b) {return !b ? a : gcd(b, a % b);
}
struct frac {ll u, d; //分子 分母 frac() { }frac(ll _u, ll _d) : u(_u), d(_d) {}
};
frac reduction(frac res) {if (res.d < 0) { //无论如何都将分母转换为正res.u = -res.u;res.d = -res.d;}if (res.u == 0) { //约定0为0/1res.d = 1;} else { //如果分子不为0, 则进行约分int t = gcd(abs(res.u), abs(res.d));res.u /= t;res.d /= t; }return res;
}
frac add(frac a, frac b) {ll u = a.u * b.d + a.d * b.u, d = a.d * b.d;return reduction(frac(u, d));
}
void showFrac(frac a) {if (a.d == 1) printf("%lld", a.u); //打印整数else if (abs(a.u) > a.d) {printf("%lld %lld/%lld", a.u / a.d, abs(a.u) % a.d, a.d);} else {printf("%lld/%lld", a.u, a.d);}
}int main() {int n, u, d;scanf("%d", &n);frac ans(0, 1), temp;while (n--) {scanf("%lld/%lld", &temp.u, &temp.d);ans = add(ans, temp);}showFrac(ans);return 0;
}

☆☆☆(复杂模拟) 1082 Read Number in Chinese (25 分) 0.25

Given an integer with no more than 9 digits, you are supposed to read it in the traditional Chinese way. Output Fu first if it is negative. For example, -123456789 is read as Fu yi Yi er Qian san Bai si Shi wu Wan liu Qian qi Bai ba Shi jiu. Note: zero (ling) must be handled correctly according to the Chinese tradition. For example, 100800 is yi Shi Wan ling ba Bai.

  • Input Specification:
    Each input file contains one test case, which gives an integer with no more than 9 digits.
  • Output Specification:
    For each test case, print in a line the Chinese way of reading the number. The characters are separated by a space and there must be no extra space at the end of the line.
  • Sample Input 1:
    -123456789
    
  • Sample Output 1:
    Fu yi Yi er Qian san Bai si Shi wu Wan liu Qian qi Bai ba Shi jiu
    
  • Sample Input 2:
    100800
    
  • Sample Output 2:
    yi Shi Wan ling ba Bai
    

题意:给定一个不超过9位的整数,你应该用传统的中文方式阅读它。这是一道复杂的模拟题,在我看来是很麻烦的。规则如下:

  • 如果在数字的某节(亿节、万节、个节)中,某个非零位(该节的千节除外)的高位是0,那么需要在该非0位的前面额外输出一个0,如8080输出ba qian ling ba shi,8008输出ba qian ling ba,10808输出yi Wan ling ba Bai ling ba
  • 每节的末尾视情况输出万或亿(个节除外)。
#include <bits/stdc++.h>
using namespace std;
string num[10] = { "ling","yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu" };
string c[6] = { "Ge","Shi", "Bai", "Qian", "Yi", "Wan" };
int J[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
vector<string> res;
int main() {int n;cin >> n;if (n == 0) {cout << "ling";return 0;}if (n < 0) {cout << "Fu ";n = -n;}int part[3];part[0]= n / 100000000; part[1]= (n % 100000000) / 10000;part[2] = n % 10000;bool zero = false; //是否在非零数字前输出合适的lingint printCnt = 0; //用于维护单词前没有空格,之后输入的单词都在前面加一个空格。for (int i = 0; i < 3; i++) {int temp = part[i]; //三个部分,每部分内部的命名规则都一样,都是X千X百X十Xfor (int j = 3; j >= 0; j--) {int curPos = 8 - i * 4 + j; //当前数字的位置if (curPos >= 9) continue; //最多九位数int cur = (temp / J[j]) % 10;//取出当前数字if (cur != 0) {if (zero) {printCnt++ == 0 ? cout<<"ling" : cout<<" ling";zero = false;}if (j == 0) //在个位,直接输出printCnt++ == 0 ? cout << num[cur] : cout << ' ' << num[cur]; else  //在其他位,还要输出十百千                            printCnt++ == 0 ? cout << num[cur] << ' ' << c[j] : cout << ' ' << num[cur] << ' ' << c[j]; } else {if (!zero && j != 0 && n / J[curPos] >= 10) zero = true;  //注意100020这样的情况}} //处理完每部分之后,最后输出单位,Yi/Wanif (i != 2 && part[i]>0) cout << ' ' << c[i + 4]; }return 0;
}

A1083 List Grades (25 分) 0.51

简单的排序查询的模拟题目。给出N个学生的姓名和ID、成绩,按照降序排列成绩,然后输出给定区间的成绩即可。

#include <bits/stdc++.h>
const int maxn = 100010;
using namespace std;
struct student {char name[15], id[15];int grade;
} stu[maxn];
bool cmp(student &a, student &b) {return a.grade > b.grade; //成绩降序; 所有成绩都不同
}
int main() {int n, ming, maxg, num = 0; scanf("%d", &n);for (int i = 0; i < n; i++) scanf("%s %s %d", stu[i].name, stu[i].id, &stu[i].grade);scanf("%d%d", &ming, &maxg);sort(stu, stu + n, cmp);for (int i = 0; i < n; i++) {if (stu[i].grade >= ming && stu[i].grade <= maxg) {printf("%s %s\n", stu[i].name, stu[i].id, stu[i].grade);num++;}}if (num == 0) printf("NONE\n");return 0;
}

A1084 Broken Keyboard (20 分) 0.41

PAT乙级的一个旧题目,坏键盘。

#include <cstdio>
/* 整数/字符哈希题目 */
int main() {char old[100], now[100];scanf("%s%s", old, now);int Hash[150] = {0};for (int i = 0, j = 0; old[i] || now[j]; ) {if (old[i] == now[j]) { i++; j++; } else if (old[i] != now[j]) {int t = (old[i] >= 'a' && old[i] <= 'z') ? old[i] - 'a' + 'A' : old[i];if (Hash[t] == 0) { printf("%c", t); Hash[t] = 1; }i++;}}printf("\n");return 0;
}

★ A1085 Perfect Sequence (25 分) 0.27

PAT乙级的一个旧题目,可以使用二分法和双指针的思想。

#include <bits/stdc++.h>
using namespace std;int main() {int n, p;scanf("%d%d", &n, &p);long long a[n];for (int i = 0; i < n; i++) scanf("%lld", &a[i]);sort(a, a + n);int i = 0, j = 0, count = 0; //枚举每一个端点while (i < n && j < n) {//j不断右移, 直到不满足条件while (j < n && a[j] <= a[i] * p) {count = max(count, j - i + 1);j++;}i++; //i右移一位} printf("%d\n", count); //打印最大长度return 0;
}

(二叉树非递归遍历+先序中序->后序) A1086 Tree Traversals Again (25 分)

An inorder binary tree traversal can be implemented in a non-recursive way with a stack. For example, suppose that when a 6-node binary tree (with the keys numbered from 1 to 6) is traversed, the stack operations are: push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop(). Then a unique binary tree (shown in Figure 1) can be generated from this sequence of operations. Your task is to give the postorder traversal sequence of this tree.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains a positive integer N (≤30) which is the total number of nodes in a tree (and hence the nodes are numbered from 1 to N). Then 2N lines follow, each describes a stack operation in the format: “Push X” where X is the index of the node being pushed onto the stack; or “Pop” meaning to pop one node from the stack.
  • Output Specification:
    For each test case, print the postorder traversal sequence of the corresponding tree in one line. A solution is guaranteed to exist. All the numbers must be separated by exactly one space, and there must be no extra space at the end of the line.
  • Sample Input:
    6
    Push 1
    Push 2
    Push 3
    Pop
    Pop
    Push 4
    Pop
    Pop
    Push 5
    Push 6
    Pop
    Pop
    
  • Sample Output:
    3 4 2 6 5 1
    

题意:用栈来模拟一棵二叉树的先序和中序遍历过程,求二叉树的后序遍历序列。每访问一个新结点就将其入栈,Push的次序就是先序遍历序列中元素的顺序。而Pop的次序就是中序遍历序列中元素的顺序。可以重建二叉树,但是我这里是直接输出后序序列

#include <bits/stdc++.h>
using namespace std;
int n;
vector<int> Pre, In;
void create() {char order[10];int data;stack<int> q;for (int i = 0; i < 2 * n; i++) {scanf("%s %d", order, &data);if (order[1] == 'u') {Pre.push_back(data);q.push(data); //先序序列}else if (order[1] == 'o') {int d = q.top();In.push_back(d); //中序序列  q.pop();}}
}
int num = 0;
void getPost(int r, int begin, int end) {if (begin > end) return;int k = begin;while (k <= end && In[k] != Pre[r]) k++;int numLeft = k - begin;getPost(r + 1, begin, k - 1);getPost(r + numLeft + 1, k + 1, end);printf("%d", Pre[r]);if (num++ < n - 1) printf(" ");
}
int main() {scanf("%d", &n);create();getPost(0, 0, n - 1);return 0;
}

★★(分数类全面总结) A1088 Rational Arithmetic (20 分) 0.30

计算两个分数的sum, difference, product and quotient。
注意点:

  • 这里一律要使用long long类型,即使是构造函数也要用long long。
  • 如果传入的分数如0/6,在做除法的时候会使得结果的分母为0,这时要输出Inf。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct frac {ll u, d;frac() {}frac(ll _u, ll _d) : u(_u), d(_d) {}
};
ll gcd(ll a, ll b) {return !b ? a : gcd(b, a % b);
}
frac reduction(frac r) {if (r.d < 0) {r.d = -r.d;r.u = -r.u;}if (r.u == 0) {r.d = 1;} else {ll t = gcd(abs(r.u), abs(r.d));r.u /= t;r.d /= t;}return r;
}
frac add(frac a, frac b) {ll u = a.u * b.d + a.d * b.u, d = a.d * b.d;return reduction(frac(u, d));
}
frac sub(frac a, frac b) {ll u = a.u * b.d - b.u * a.d, d = a.d * b.d;return reduction(frac(u, d));
}
frac mul(frac a, frac b) {ll u = a.u * b.u, d = a.d * b.d;return reduction(frac(u, d));
}
frac div(frac a, frac b) {ll u = a.u * b.d, d = b.u * a.d;return reduction(frac(u, d));
}
void showFrac(frac a) {a = reduction(a);if (a.u < 0) printf("(");if (a.d == 0) printf("Inf");else if (a.d == 1) printf("%lld", a.u);else if (abs(a.u) > a.d) {printf("%lld %lld/%lld", a.u / a.d, abs(a.u) % a.d, a.d);} else {printf("%lld/%lld", a.u, a.d);}if (a.u < 0) printf(")");
}
int main() {ll u1, d1, u2, d2;scanf("%lld/%lld %lld/%lld", &u1, &d1, &u2, &d2);frac r1(u1, d1), r2(u2, d2);r1 = reduction(r1), r2 = reduction(r2);showFrac(r1); printf(" + "); showFrac(r2); printf(" = "); showFrac(add(r1, r2)); cout << endl;showFrac(r1); printf(" - "); showFrac(r2); printf(" = "); showFrac(sub(r1, r2)); cout << endl;showFrac(r1); printf(" * "); showFrac(r2); printf(" = "); showFrac(mul(r1, r2)); cout << endl;showFrac(r1); printf(" / "); showFrac(r2); printf(" = "); showFrac(div(r1, r2)); cout << endl;return 0;
}

★(排序算法) A1089 Insert or Merge (25 分)

Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. Each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there. It repeats until no input elements remain.

Merge sort works as follows: Divide the unsorted list into N sublists, each containing 1 element (a list of 1 element is considered sorted). Then repeatedly merge two adjacent sublists to produce new sorted sublists until there is only 1 sublist remaining.

Now given the initial sequence of integers, together with a sequence which is a result of several iterations of some sorting method, can you tell which sorting method we are using?

  • Input Specification:
    Each input file contains one test case. For each case, the first line gives a positive integer N (≤100). Then in the next line, N integers are given as the initial sequence. The last line contains the partially sorted sequence of the N numbers. It is assumed that the target sequence is always ascending. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print in the first line either “Insertion Sort” or “Merge Sort” to indicate the method used to obtain the partial result. Then run this method for one more iteration and output in the second line the resuling sequence. It is guaranteed that the answer is unique for each test case. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.
  • Sample Input 1:
    10
    3 1 2 8 7 5 9 4 6 0
    1 2 3 7 8 5 9 4 6 0
    
  • Sample Output 1:
    Insertion Sort
    1 2 3 5 7 8 9 4 6 0
    
  • Sample Input 2:
    10
    3 1 2 8 7 5 9 4 0 6
    1 3 2 8 5 7 4 9 0 6
    
  • Sample Output 2:
    Merge Sort
    1 2 3 8 4 5 7 9 0 6
    

给定一个初始序列和一个经过插入排序或者归并排序多轮迭代后的目标序列,问它是有哪种排序方法产生的,然后输出下一轮迭代后会产生的序列。

  • 注意点:初始序列不参与与目标序列的比较,不然会错测试点。如:

    //input
    3 4 2 1
    3 4 2 1
    //output
    Insertion Sort
    2 3 4 1
    
  • 因为数据范围比较小,归并排序这里可以用sort直接替代归并函数,使用非递归方式。
#include <bits/stdc++.h>
using namespace std;
int n, origin[110], target[110], temp[110];bool isSame(int a[], int target[]) {for (int i = 0; i < n; i++) if (a[i] != target[i]) return false;return true;
}void trace(int a[], int k) {printf(k == 1 ? "Insertion Sort\n" : "Merge Sort\n");for (int i = 0; i < n; i++) {if (i > 0) printf(" ");printf("%d", a[i]);} printf("\n");
}bool InsertionSort() {bool flag = false; //是否迭代到了目标数组 for (int i = 1; i < n; i++) { //不比对初始序列是否是目标序列if (i != 1 && isSame(temp, target)) flag = true;int t = temp[i], j;for (j = i; j > 0 && t < temp[j - 1]; j--) temp[j] = temp[j - 1];temp[j] = t;if (flag) { //如果相同, 在完成上面的又一轮迭代后输出 trace(temp, 1);return true;}}return false;
}
void MergeSort() {bool flag = false; //归并排序第一步:两两分组排序for (int step = 2; step / 2 <= n; step *= 2) { //不比对初始序列是否是目标序列if (step != 2 && isSame(temp, target)) flag = true;for (int i = 0; i < n; i += step) {sort(temp + i, temp + min(i + step, n));} if (flag) { //如果相同, 在完成上面的又一轮迭代后输出 trace(temp, 2);return;}}
}int main() {scanf("%d", &n);for (int i = 0; i < n; i++) {scanf("%d", &origin[i]);temp[i] = origin[i]; //备份 }for (int i = 0; i < n; i++) scanf("%d", &target[i]);if (!InsertionSort()) {for (int i = 0; i < n; i++) temp[i] = origin[i]; //复原备份 MergeSort(); }return 0;
}

(静态树+DFS遍历) A1090 Highest Price in Supply Chain (25 分) 0.41

A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone involved in moving a product from supplier to customer.

Starting from one root supplier, everyone on the chain buys products from one’s supplier in a price P and sell or distribute them in a price that is r% higher than P. It is assumed that each member in the supply chain has exactly one supplier except the root supplier, and there is no supply cycle.

Now given a supply chain, you are supposed to tell the highest price we can expect from some retailers.

  • Input Specification:
    Each input file contains one test case. For each case, The first line contains three positive numbers: N (≤10​5​​), the total number of the members in the supply chain (and hence they are numbered from 0 to N−1); P, the price given by the root supplier; and r, the percentage rate of price increment for each distributor or retailer. Then the next line contains N numbers, each number S​i​​ is the index of the supplier for the i-th member. S​root​​ for the root supplier is defined to be −1. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print in one line the highest price we can expect from some retailers, accurate up to 2 decimal places, and the number of retailers that sell at the highest price. There must be one space between the two numbers. It is guaranteed that the price will not exceed 10​10​​.
  • Sample Input:
    9 1.80 1.00
    1 5 4 4 -1 4 5 3 6
    
  • Sample Output:
    1.85 2
    

题意:给出一棵供应分销销售的树,树根唯一,在树根处价格为P,每向下一层都会增加r%,求出所有叶子结点中的最高价格和这个价格的叶结点个数。

  • 我的写法是用一个结构体数组,结构体中的price表示可能的叶子结点的价格;每次遍历到叶子结点的时候存储价格到叶子结点中,更新最高价格;最后遍历最高价格的结点数目。
  • 另外一种方法是,设置全局变量maxDepth,表示最大深度,以num表示最大深度的结点个数;每次遍历到叶子结点时,更新最大深度,如果大于最大深度maxDepth,则置num为1;如果等于最大深度,则num++
  • 与A1079、A1106题目相似。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct Node {double price;vector<int> child;Node() { price = 0; }Node(double p) { price = p; }
} node[maxn];double ans = 0.0, p, r;
void DFS(int root, double price) {if (node[root].child.size() == 0) {node[root].price = price;if (price > ans) ans = price;return;}else {for (int i = 0; i < node[root].child.size(); i++) {int child = node[root].child[i];DFS(child, price * (1.0 + r / 100.0));}}
}int main() {int n, father, root;scanf("%d%lf%lf", &n, &p, &r);for (int i = 0; i < n; i++) {scanf("%d", &father);if (father == -1) root = i; //the root supplier is defined to be -1else node[father].child.push_back(i);} DFS(root, p);int cnt = 0;for (int i = 0; i < n; i++) if (node[i].price == ans) cnt++;printf("%.2lf %d\n", ans, cnt);return 0;
}

A1092 To Buy or Not to Buy (20 分) 0.54

PAT乙级的一个旧题目,B1039。

#include <cstdio>
int main() {char buy[1010], want[1010];int hash[130] = {0};scanf("%s", buy); scanf("%s", want);for (int i = 0; want[i]; i++) hash[want[i]]++;for (int i = 0; buy[i]; i++) hash[buy[i]]--;int pos = 0, neg = 0; //如果哈希表全部<=0, Yes, 负数之和即多的珠子for (int i = 0; i < 128; i++) {if (hash[i] > 0) pos += hash[i];else if (hash[i] < 0) neg += hash[i];}if (pos) printf("No %d\n", pos); //只要存在pos, 说明缺少想要的珠子else printf("Yes %d\n", -neg); //strlen(buy) - strlen(want)return 0;
}

A1093 Count PAT’s (25 分)

The string APPAPT contains two PAT’s as substrings. The first one is formed by the 2nd, the 4th, and the 6th characters, and the second one is formed by the 3rd, the 4th, and the 6th characters.

Now given any string, you are supposed to tell the number of PAT’s contained in the string.

  • Input Specification:
    Each input file contains one test case. For each case, there is only one line giving a string of no more than 10​5​​ characters containing only P, A, or T.
  • Output Specification:
    For each test case, print in one line the number of PAT’s contained in the string. Since the result may be a huge number, you only have to output the result moded by 1000000007.
  • Sample Input:
    APPAPT
    
  • Sample Output:
    2
    

做过的题目。巧妙利用左右的递推关系。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100100;
string s; //leftP[i]/rightT[i]表示在i之前或之后的P/T的个数
int leftP[maxn] = {0}, rightT[maxn] = {0}; int main() {getline(cin, s);for (int i = 1; i < s.size(); i++) {if (s[i - 1] == 'P') leftP[i] = leftP[i - 1] + 1;else leftP[i] = leftP[i - 1];}for (int i = s.size() - 2; i >= 0; i--) {if (s[i + 1] == 'T') rightT[i] = rightT[i + 1] + 1;else rightT[i] = rightT[i + 1];}int ans = 0;for (int i = 0; i < s.size(); i++) {if (s[i] == 'A') { //PAT的数目为A左边的P数目*A右边的T的数目ans = (ans + (leftP[i] * rightT[i])) % 1000000007;}}printf("%d\n", ans);return 0;
}

(静态树+DFS) A1094 The Largest Generation (25 分) 0.41

A family hierarchy is usually presented by a pedigree tree where all the nodes on the same level belong to the same generation. Your task is to find the generation with the largest population.

  • Input Specification:
    Each input file contains one test case. Each case starts with two positive integers N (<100) which is the total number of family members in the tree (and hence assume that all the members are numbered from 01 to N), and M (<N) which is the number of family members who have children. Then M lines follow, each contains the information of a family member in the following format:

    ID K ID[1] ID[2] ... ID[K]
    

    where ID is a two-digit number representing a family member, K (>0) is the number of his/her children, followed by a sequence of two-digit ID’s of his/her children. For the sake of simplicity, let us fix the root ID to be 01. All the numbers in a line are separated by a space.

  • Output Specification:
    For each test case, print in one line the largest population number and the level of the corresponding generation. It is assumed that such a generation is unique, and the root level is defined to be 1.
  • Sample Input:
    23 13
    21 1 23
    01 4 03 02 04 05
    03 3 06 07 08
    06 2 12 13
    13 1 21
    08 2 15 16
    02 2 09 10
    11 2 19 20
    17 1 22
    05 1 11
    07 1 14
    09 1 17
    10 1 18
    
  • Sample Output:
    9 4
    

题意:给出一棵家族树,求出树中结点个数最多的一层,输出该层的结点个数和层号。

思路:使用DFS或者BFS都可以,DFS写起来简单一些,不过都要开一个全局数组,记录每一层的结点个数。这里的DFS中的depth表示要遍历的这个结点下一层,因为我已经在main函数中记录了根结点的第一层了。如果用depth表示当前访问结点root的层次,就要将增加第level层的结点数的代码移至函数开头,主函数中也不需要预先设置levelPopu[1]。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
vector<int> node[maxn]; //the root level is defined to be 1
int n, m, levelPopu[maxn] = {0}, maxLevel = 1;
void DFS(int root, int depth) { //depth表示要遍历的这个结点下一层 for (int i = 0; i < node[root].size(); i++) {int child = node[root][i];levelPopu[depth]++;if (depth > maxLevel) maxLevel = depth;DFS(child, depth + 1);}
}int main() {int id, k, child; scanf("%d%d", &n, &m);for (int i = 0; i < m; i++) {scanf("%d%d", &id, &k); for (int j = 0; j < k; j++) {scanf("%d", &child);node[id].push_back(child); } }memset(levelPopu, 0, sizeof(levelPopu));levelPopu[1] = 1; //根结点第一层有1个结点 DFS(1, 2);  //fix the root ID to be 01int t = 1;for (int i = 1; i <= maxLevel; i++) if (levelPopu[i] > levelPopu[t]) t = i; printf("%d %d\n", levelPopu[t], t);return 0;
}

★★★(排序+复杂模拟+map应用) A1095 Cars on Campus (30 分) 0.25

Zhejiang University has 8 campuses and a lot of gates. From each gate we can collect the in/out times and the plate numbers of the cars crossing the gate. Now with all the information available, you are supposed to tell, at any specific time point, the number of cars parking on campus, and at the end of the day find the cars that have parked for the longest time period.

  • Input Specification:
    Each input file contains one test case. Each case starts with two positive integers N (≤10​4​​), the number of records, and K (≤8×10​4​​) the number of queries. Then N lines follow, each gives a record in the format:

    plate_number hh:mm:ss status
    

    where plate_number is a string of 7 English capital letters or 1-digit numbers; hh:mm:ssrepresents the time point in a day by hour:minute:second, with the earliest time being 00:00:00 and the latest 23:59:59; and status is either in or out.

    Note that all times will be within a single day. Each in record is paired with the chronologically next record for the same car provided it is an out record. Any in records that are not paired with an out record are ignored, as are out records not paired with an in record. It is guaranteed that at least one car is well paired in the input, and no car is both in and out at the same moment. Times are recorded using a 24-hour clock.

    Then K lines of queries follow, each gives a time point in the format hh:mm:ss. Note: the queries are given in ascending order of the times.

  • Output Specification:
    For each query, output in a line the total number of cars parking on campus. The last line of output is supposed to give the plate number of the car that has parked for the longest time period, and the corresponding time length. If such a car is not unique, then output all of their plate numbers in a line in alphabetical order, separated by a space.

  • Sample Input:

    16 7
    JH007BD 18:00:01 in
    ZD00001 11:30:08 out
    DB8888A 13:00:00 out
    ZA3Q625 23:59:50 out
    ZA133CH 10:23:00 in
    ZD00001 04:09:59 in
    JH007BD 05:09:59 in
    ZA3Q625 11:42:01 out
    JH007BD 05:10:33 in
    ZA3Q625 06:30:50 in
    JH007BD 12:23:42 out
    ZA3Q625 23:55:00 in
    JH007BD 12:24:23 out
    ZA133CH 17:11:22 out
    JH007BD 18:07:01 out
    DB8888A 06:30:50 in
    05:10:00
    06:30:50
    11:00:00
    12:23:42
    14:00:00
    18:00:00
    23:59:00
    
  • Sample Output:

1
4
5
2
1
0
1
JH007BD ZD00001 07:20:09

题目大意:给出n个车牌号、时间点、进出状态的记录,然后查询k个时间点的校园内的车辆个数。最后还要输出在校园里面呆的时间最长的车的车牌号,以及呆了多久的时间。如果有多辆车就按照它的字母从小到大输出车牌。
注意点:

  • 将时间统一转换成以秒为单位可以节省很多时间,如比较时间的早晚、计算时间的间隔等,转换回去时分秒也很简单;
  • 使用all数组记录所有记录的车牌号、进或出的时刻、进出标志,对其先按照车牌号从小到大排序,再按照时间从小到大排序;使用valid数组记录所有有效的进出记录,用map来记录每个车牌号对应的总停留时间;不断更新最长总停留时间;然后把valid数组按时间从小到答排序;
  • 因为查询的时间点按时间顺序递增,所以可以设置numCar记录车辆数量,从头开始,到该时间点为止,中间每进入一辆车就+1,每出一辆车就-1,然后就得到了该时间点的车辆数目;之后接着往下,统计到下一个时间点的车辆数量。
#include <bits/stdc++.h>
using namespace std;
struct car {string pla;int sec; //记录的时刻(以秒为单位) int stat; //0 in 1 out
};
const int H = 3600, M = 60, maxn = 10010;
bool cmp(const car &a, const car &b) {if (a.pla != b.pla) return a.pla < b.pla; //按字典序从小到大排序 else return a.sec < b.sec; //按时间从早到晚排序
}
bool cmp2(const car &a, const car &b) {return a.sec < b.sec;
}
car all[maxn], valid[maxn];int main() {int n, k, hh, mm, ss;scanf("%d%d", &n, &k);map<string, int> parkTime; //记录每辆车的总停留时间 char s[5];for (int i = 0; i < n; i++) {cin >> all[i].pla;scanf("%d:%d:%d %s", &hh, &mm, &ss, s);all[i].sec = hh * H + mm * M + ss;if (s[0] == 'i') all[i].stat = 0;else all[i].stat = 1;} sort(all , all + n, cmp); int len = 0, maxTime = -1; //有效记录的条数 最大总停留时间 for (int i = 0; i < n - 1; i++) {if (all[i].pla == all[i + 1].pla && all[i].stat == 0 && all[i + 1].stat == 1) {valid[len++] = all[i];  //i和i+1是配对的 valid[len++] = all[i + 1];parkTime[all[i].pla] += all[i + 1].sec - all[i].sec; //此次停留时间 if (parkTime[all[i].pla] > maxTime) maxTime = parkTime[all[i].pla]; //更新最大停留时间 }}sort(valid , valid + len, cmp2);int now = 0, numCar = 0; //now指向不超过当前查询时间的记录, numCar为当前校园内车辆数 while (k--) {scanf("%d:%d:%d", &hh, &mm, &ss);int time = hh * H + mm * M + ss;while (now < len && valid[now].sec <= time) {if (valid[now].stat == 0) numCar++; //车辆留下 else numCar--; //车辆离开 now++; //指向下一条记录 }printf("%d\n", numCar); //输出该时刻校园内车辆数 }for (auto it : parkTime) //输出最长总停留时间的车牌号 if (it.second == maxTime) printf("%s ", it.first.c_str()); printf("%02d:%02d:%02d\n", maxTime / H, maxTime % H / M, maxTime % M); //输出最长总停留时间 return 0;
}

☆ A1096 Consecutive Factors (20 分)

Among all the factors of a positive integer N, there may exist several consecutive numbers. For example, 630 can be factored as 3×5×6×7, where 5, 6, and 7 are the three consecutive numbers. Now given any positive N, you are supposed to find the maximum number of consecutive factors, and list the smallest sequence of the consecutive factors.

  • Input Specification:
    Each input file contains one test case, which gives the integer N (1<N<2​31​​).
  • Output Specification:
    For each test case, print in the first line the maximum number of consecutive factors. Then in the second line, print the smallest sequence of the consecutive factors in the format factor[1]factor[2]…*factor[k], where the factors are listed in increasing order, and 1 is NOT included.
  • Sample Input:
    630
    
  • Sample Output:
    3
    5*6*7
    

这个题与其说是求一个数的因子,倒不如说是求一个数能被多少连续整数的乘积整除。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;int main() {ll n, maxLen = 0, start = 0;scanf("%lld", &n);ll sqr = (ll)sqrt(1.0 * n); //N不会被除自己以外的大于根号n的数整除for (int i = 2; i <= sqr; i++) {ll temp = 1, j = i; //temp为当前连续整数的乘积while (1) {temp *= j; //获得当前连续整数的乘积if (n % temp != 0) break; //如果不能整除n, 那么结束运算if (j - i + 1 > maxLen) { //发现了更长的长度 maxLen = j - i + 1; //更新最长长度start = i; //更新第一个整数} j++; //连续的下一个整数 }}if (maxLen == 0) printf("1\n%lld", n); //最大长度为0, 说明根号n范围内没有解else {printf("%lld\n", maxLen); //输出最大长度for (int i = 0; i < maxLen; i++) { if (i > 0) printf("*");printf("%lld", start + i); //输出[start, start+maxLen)}}return 0;
}

(静态链表) A1097 Deduplication on a Linked List (25 分) 0.32

Given a singly linked list L with integer keys, you are supposed to remove the nodes with duplicated absolute values of the keys. That is, for each value K, only the first node of which the value or absolute value of its key equals K will be kept. At the mean time, all the removed nodes must be kept in a separate list. For example, given L being 21→-15→-15→-7→15, you must output 21→-15→-7, and the removed list -15→15.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains the address of the first node, and a positive N (≤10​5​​) which is the total number of nodes. The address of a node is a 5-digit nonnegative integer, and NULL is represented by −1.
    Then N lines follow, each describes a node in the format:

    Address Key Next
    

    where Address is the position of the node, Key is an integer of which absolute value is no more than 10​4​​, and Next is the position of the next node.

  • Output Specification:
    For each case, output the resulting linked list first, then the removed list. Each node occupies a line, and is printed in the same format as in the input.
  • Sample Input:
    00100 5
    99999 -7 87654
    23854 -15 00000
    87654 15 -1
    00000 -15 99999
    00100 21 23854
    
  • Sample Output:
    00100 21 23854
    23854 -15 99999
    99999 -7 -1
    00000 -15 87654
    87654 15 -1
    

题意:去重,去除链表中绝对值相等的结点,仅保留第一个该结点,最后输出去重后的链表和删除的链表。

我的方法如下,操作地址表,如果该结点值的绝对值第一次出现,就存储其地址到结果地址表result中,不然就存储到删除地址表removed中。然后依次输出result和removed中的地址、对应的key和next。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct Node {int key, next;
} node[maxn];
map<int, int> dedup;
int result[maxn], ls = 0, removed[maxn], lr = 0;int main() {int n, firstAddr, addr;scanf("%d%d", &firstAddr, &n);for (int i = 0; i < n; i++) {scanf("%d", &addr);scanf("%d%d", &node[addr].key, &node[addr].next);}for (int i = firstAddr; i != -1; i = node[i].next) {if (dedup[abs(node[i].key)] == 0) {dedup[abs(node[i].key)] = 1;result[ls++] = i;} else removed[lr++] = i;}result[ls++] = -1, removed[lr++] = -1;for (int i = 0; i < ls - 1; i++) {printf("%05d %d ", result[i], node[result[i]].key);if (result[i + 1] == -1) printf("-1\n");else printf("%05d\n", result[i + 1]);}for (int i = 0; i < lr - 1; i++) {printf("%05d %d ", removed[i], node[removed[i]].key);if (removed[i + 1] == -1) printf("-1\n");else printf("%05d\n", removed[i + 1]);}return 0;
}

运用书上的模版。这里排序的部分必须小心。排序的主键num的初始化可以在结构体声明的时候做。然后遍历整条链表,给主键num赋值,同时计数。接着排序,将有效结点排在左边,无效结点排在右边,有效结点中的去重后的链表结点排在最左边。最后输出有效结点即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct Node {int addr, key, next, num = 2 * maxn;
} node[maxn];
bool cmp(Node &a, Node &b) { return a.num < b.num; }map<int, int> dedup;
int main() {int firstAddr, n, a, cnt1 = 0, cnt2 = 0, cnt;scanf("%d%d", &firstAddr, &n);for (int i = 0; i < n; i++) {scanf("%d", &a);scanf("%d%d", &node[a].key, &node[a].next);node[a].addr = a;}for (int i = firstAddr; i != -1; i = node[i].next) {if (dedup[abs(node[i].key)] == 0) {dedup[abs(node[i].key)] = 1;node[i].num = cnt1;cnt1++;} else {node[i].num = maxn + cnt2;cnt2++;}}sort(node, node + maxn, cmp);cnt = cnt1 + cnt2;for (int i = 0; i < cnt; i++) {if (i != cnt1 -1 && i != cnt - 1) printf("%05d %d %05d\n", node[i].addr, node[i].key, node[i + 1].addr);else printf("%05d %d -1\n", node[i].addr, node[i].key);}return 0;
}

(建堆+堆排序+排序算法特点) A1098 Insertion or Heap Sort (25 分) 0.32

Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. Each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there. It repeats until no input elements remain.

Heap sort divides its input into a sorted and an unsorted region, and it iteratively shrinks the unsorted region by extracting the largest element and moving that to the sorted region. it involves the use of a heap data structure rather than a linear-time search to find the maximum.

Now given the initial sequence of integers, together with a sequence which is a result of several iterations of some sorting method, can you tell which sorting method we are using?

  • Input Specification:
    Each input file contains one test case. For each case, the first line gives a positive integer N (≤100). Then in the next line, N integers are given as the initial sequence. The last line contains the partially sorted sequence of the N numbers. It is assumed that the target sequence is always ascending. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print in the first line either “Insertion Sort” or “Heap Sort” to indicate the method used to obtain the partial result. Then run this method for one more iteration and output in the second line the resulting sequence. It is guaranteed that the answer is unique for each test case. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.
  • Sample Input 1:
    10
    3 1 2 8 7 5 9 4 6 0
    1 2 3 7 8 5 9 4 6 0
    
  • Sample Output 1:
    Insertion Sort
    1 2 3 5 7 8 9 4 6 0
    
  • Sample Input 2:
10
3 1 2 8 7 5 9 4 6 0
6 4 5 1 0 3 2 7 8 9
  • Sample Output 2:

    Heap Sort
    5 4 3 1 0 2 6 7 8 9
    

题意:给出一个初始排序序列,可以对它使用插入排序或堆排序进行排序,现在给出一个序列,判断是由哪种方法产生的,并给出下一步的序列。
普通思路:和A1089类似,模拟插入排序和堆排序的每一步过程。

  • 可以在插入部分直接使用sort替代;
  • 初始序列不参与比较是否与目标序列相同;
  • 要求熟练掌握建堆和堆操作的模版,以及堆排序的写法
#include <bits/stdc++.h>
using namespace std;
const int N = 111;
int ori[N], temp[N], targ[N], n; //原始数组、备份、目标数组
bool isSame(int a[], int b[]) {for (int i = 1; i <= n; i++) if (a[i] != b[i]) return false;return true;
}
bool showArray(int a[]) {for (int i = 1; i <= n; i++) {printf("%d", a[i]);if (i < n) printf(" ");}printf("\n");
}
bool insertionSort() {bool f = false;for (int i = 2; i <= n; i++) { //进行n-1躺排序 if (i != 2 && isSame(temp, targ)) f = true;sort(temp, temp + i + 1); //插入部分用sort代替if (f == true) return true;}return false; //无法达到目标数组
}void downAdjust(int low, int high) {int i = low, j = i * 2; //i为欲调整结点, j为左孩子结点while (j <= high) { //存在左孩子结点 if (j + 1 <= high && temp[j + 1] > temp[j]) j = j + 1;if (temp[j] > temp[i]) { //孩子结点中最大的结点值比父结点大 swap(temp[j], temp[i]); i = j; //令i为jj = i * 2; //j为i的左孩子结点, 进入下一层 } else break; //孩子结点的权值均比父亲结点的小, 调整结束 }
}
void heapSort() {bool f = false;for (int i = n / 2; i >= 1; i--) downAdjust(i, n); //建堆 for (int i = n; i > 1; i--) { //倒着枚举 if (i != n && isSame(temp, targ)) f = true; swap(temp[i], temp[1]); //交换heap[i]与栈顶 downAdjust(1, i - 1); //调整栈顶 if (f == true) {showArray(temp);return;  } }
}int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%d", &ori[i]);temp[i] = ori[i]; //temp数组为备份, 排序在temp上进行 }for (int i = 1; i <= n; i++) scanf("%d", &targ[i]);if (insertionSort()) {printf("Insertion Sort\n");showArray(temp);} else {printf("Heap Sort\n");for (int i = 1; i <= n; i++) temp[i] = ori[i];heapSort();}return 0;
}

更强的思路:

  • 插入排序的特点:b数组前面的顺序从小到大,后面的顺序则一定与原数组一致,不一定有序。所以只需要遍历一下前面几位,遇到不是从小到大的时候,比较b数组和a数组对应位置上的值是否相等,相等说明是插入排序;否则为堆排序。
  • 插入排序的下一步:把第一个不符合从小到大的顺序的元素插入到前面的有序集合的合适位置,作为替代,只需对前面的部分+后面这一位进行sort即可。
  • 堆排序的特点:后面从小到大,前面的顺序不一定,但是(因为要从小到大排序)符合大顶堆的特性,未排序序列的最大值一定为b[1]
  • 堆排序的下一步:只需要从n开始往前面找到第一个小于b[1]的数字,将它与第一个数字交换,然后对数组b从1到p-1区间进行一次向下调整即可。
  • 向下调整是指:对于大顶堆,不断比较当前结点与自己的左右孩子结点中较大的那个结点,哪个大。如果孩子大,就交换孩子结点和自己。然后继续,直到达到区间的最大值为止。
#include <bits/stdc++.h>
using namespace std;
void downAdjust(vector<int> &b, int low, int high) {int i = low, j = i * 2; //i为欲调整结点, j为i的左孩子结点while (j <= high) {if (j + 1 <= high && b[j] < b[j + 1]) j = j + 1; //大顶堆, 找到大的孩子结点if (b[i] >= b[j]) break; //如果孩子结点中最大的结点值比父结点小, 无需调整swap(b[i], b[j]); //交换最大权值的孩子结点与父结点i = j; j = i * 2; //进入下一层}
}
int main() {int n, p = 2;scanf("%d", &n);vector<int> a(n + 1), b(n + 1);for (int i = 1; i <= n; i++) scanf("%d", &a[i]);for (int i = 1; i <= n; i++) scanf("%d", &b[i]);while (p <= n && b[p - 1] <= b[p]) p++; //如果b数组前面的顺序从小到大int index = p; //找到第1个不不满足条件的下标p并且赋值给indexwhile (p <= n && a[p] == b[p]) p++; //b数组后面的顺序和a数组一样if (p == n + 1) { //是插入排序printf("Insertion Sort\n");sort(b.begin() + 1, b.begin() + index + 1);} else {printf("Heap Sort\n");p = n;while (p > 2 && b[p] >= b[1]) p--; //找到第一个小于b[1]的数字下标swap(b[1], b[p]);downAdjust(b, 1, p - 1);}printf("%d", b[1]);for(int i = 2; i <= n; i++)printf(" %d", b[i]);return 0;
}

(静态二叉查找树+层序遍历) A1099 Build A Binary Search Tree (30 分) 0.57

A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties:

The left subtree of a node contains only nodes with keys less than the node’s key.
The right subtree of a node contains only nodes with keys greater than or equal to the node’s key.
Both the left and right subtrees must also be binary search trees.

Given the structure of a binary tree and a sequence of distinct integer keys, there is only one way to fill these keys into the tree so that the resulting tree satisfies the definition of a BST. You are supposed to output the level order traversal sequence of that tree. The sample is illustrated by Figure 1 and 2.

  • Input Specification:
    Each input file contains one test case. For each case, the first line gives a positive integer N (≤100) which is the total number of nodes in the tree. The next N lines each contains the left and the right children of a node in the format left_index right_index, provided that the nodes are numbered from 0 to N−1, and 0 is always the root. If one child is missing, then −1 will represent the NULL child pointer. Finally N distinct integer keys are given in the last line.
  • Output Specification:
    For each test case, print in one line the level order traversal sequence of that tree. All the numbers must be separated by a space, with no extra space at the end of the line.
  • Sample Input:
    9
    1 6
    2 3
    -1 -1
    -1 4
    5 -1
    -1 -1
    7 -1
    -1 8
    -1 -1
    73 45 11 58 82 25 67 38 42
    
  • Sample Output:
    58 25 82 11 38 67 45 73 42
    

题意:二叉树有N个结点,给出每个结点的左右孩子结点的编号,即二叉树的结构,然后给出一个N个整数序列,要求将N个整数填充到相应二叉树的结点中,使之成为一棵二叉查找树。最后输出二叉查找树的层序遍历序列。
思路:

  • 题目给出了结点编号,使用二叉树的静态写法更方便;
  • 题目根结点默认为0,不需要寻找根结点;
  • 一棵二叉查找树的中序遍历序列是从小到大有序的,利用这个性质,将给定的整数序列从小到大排序,然后对给定的二叉树进行中序遍历,边遍历边填充整数,就可以形成二叉查找树。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
struct Node {int data;int left, right;
} tree[maxn];
int n, left, right, d[maxn], p = 0;
void InOrder(int root) {if (root != -1) {InOrder(tree[root].left);tree[root].data = d[p++]; InOrder(tree[root].right); }
}
vector<int> Level;
void LevelOrder(int root) {queue<int> q;q.push(root);while (!q.empty()) {int v = q.front(); q.pop();Level.push_back(tree[v].data);if (tree[v].left != -1) q.push(tree[v].left);if (tree[v].right != -1) q.push(tree[v].right);}
}int main() {scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d%d", &tree[i].left, &tree[i].right);for (int i = 0; i < n; i++) scanf("%d", &d[i]);sort(d, d + n);    //排序, 从小到大排序InOrder(0);LevelOrder(0);for (int i = 0; i < Level.size(); i++) {if (i > 0) printf(" ");printf("%d", Level[i]);}return 0;
}

【PAT甲级】A1051-A1100刷题记录相关推荐

  1. 【PAT甲级】A1001-A1050刷题记录

    文章目录 A1001 A+B Format (20 分) 0.25 ★(一元多项式加法) A1002 A+B for Polynomials (25 分) 0.21 (单源最短路Dijkstra+边权 ...

  2. 【PAT甲级】A1101-A1155刷题记录

    文章目录 (递推) A1101 Quick Sort (25 分) 0.23 (静态二叉树+遍历) A1102 Invert a Binary Tree (25 分) 0.51 (数学问题) A110 ...

  3. 2020年9月PAT甲级满分必备刷题技巧

    2020年7月的考试结束了,除了本次的考题更新,短期内不会更新. [7月题目的特点:首次线上考试,没出链表.树相关的模板题,第2到4题背景新颖,大大降低了抄袭历年代码的可能性,可以看作是线上考试的新趋 ...

  4. PAT甲级官网 刷题(1)

    PAT1138 Postorder Traversal   根据前序和中序遍历确定二叉树,模板题,要求输出第一个后序遍历的节点.TIPS:利用map来映射后序遍历在中序遍历中的位置,否则复杂度过高导致 ...

  5. PAT甲级官网 刷题(3)

    PAT1130 Infix Expression   递归,但是官网有个测试点没过,不知道错在哪里,欢迎指出 #include<iostream>#define ac cin.tie(0) ...

  6. PAT甲级刷题记录-(AcWing)-(Day06树 8题)

    PAT甲级刷题记录-(AcWing)-(Day06树 8题) 课程来源AcWing 其中AcWing中的题目为翻译好的中文题目 今日刷题列表 1110 Complete Binary Tree 111 ...

  7. 小峰峰的pat甲级刷题记录1020

    小峰峰的pat甲级刷题记录1020 方法一:通过后序和中序序列构建树,再层序输出 #include<iostream> #include<vector> using names ...

  8. Python刷题记录(81-90)

    Python刷题记录(81-90) 题目来源PTA平台 PAT (Basic Level) Practice (中文) @TOC 1081 检查密码 本题要求你帮助某网站的用户注册模块写一个密码合法性 ...

  9. 算法笔记CodeUp第一至第六章刷题记录

    文章目录 <算法笔记>2.2小节--C/C++快速入门->顺序结构 1.例题1-1-1 按要求输出信息(1) 2.例题1-1-2 按要求输出信息(2) 3.例题1-2-1 求两个整数 ...

最新文章

  1. dubbo入门学习笔记之入门demo(基于普通maven项目)
  2. Linux命令行下登录ssl加密的ftp
  3. .NET中委托写法的演变(上):委托与匿名方法
  4. 计算机体系结构 -- 第一章3 -- 设计的定量4个原则
  5. 操作 实例 / dom
  6. R语言第五讲 之R语言 变量
  7. cf1107e uva10559区间dp升维
  8. 计算机专业说课,计算机专业课程说课.ppt
  9. java笔记:SpringSecurity应用(二)
  10. 为什么中国电气自动化工程师这么难招
  11. zemax---透镜基础篇
  12. 永久关闭“WPS热点”的显示_我是亲民_新浪博客
  13. 树莓派接入VGA显示器画面不全偏左的问题与VGA转HDMI连接显示器无法输出声音的解决
  14. 忘记HP服务器ilo密码?如何在不重启服务器的情况下重置ilo登录密码
  15. JSONObject.toBean() 把jsonobject转换成实体类
  16. 提交工程到git的分支上
  17. html引导蒙层,web开发中实现图标点击态蒙层
  18. csgo跳投指令_CSGO一键跳投脚本代码,附CFG文件下载
  19. 大专生出身?java如何导入excel数据
  20. 2.6 Python 基本数据类型

热门文章

  1. 快递100接口的简单使用:
  2. 基于ASP.NET技术的企业办公自动化系统的设计
  3. 【Git 问题及解决方案】fatal: unable to access ‘https://github.com/xxx/yyy.git/‘: OpenSSL SSL_read: Con
  4. 真正标准,规范的企业网站建设合同书
  5. matlab画分形minkowski,分形系列之matlab绘制koch曲线
  6. 6. 调整图像亮度和对比度(OpenCV基础课程视频学习)
  7. SpiFlash同步/异步读写单片机裸机实例
  8. Windows x86 环境 虚拟机 安装银河麒麟V10 arm架构系统
  9. 基于 jieba 和 word_cloud 生成《人民的名义》小说词云
  10. 2.面向性能的设计与开发