7-1 Forever (20 分)
時間限制:3000 ms 內存限制:64 MB
"Forever number" is a positive integer A with K digits, satisfying the following constrains:
- the sum of all the digits of A is m;
- the sum of all the digits of A+1 is n; and
- the greatest common divisor of m and n is a prime number which is greater than 2.
Now you are supposed to find these forever numbers.
Input Specification:
Each input file contains one test case. For each test case, the first line contains a positive integer N (≤5). Then N lines follow, each gives a pair of K (3<K<10) and m (1<m<90), of which the meanings are given in the problem description.
Output Specification:
For each pair of K and m, first print in a line Case X, where X is the case index (starts from 1). Then print n and A in the following line. The numbers must be separated by a space. If the solution is not unique, output in the ascending order of n. If still not unique, output in the ascending order of A. If there is no solution, output No Solution.
Sample Input:
2
6 45
7 80
Sample Output:
Case 1
10 189999
10 279999
10 369999
10 459999
10 549999
10 639999
10 729999
10 819999
10 909999
Case 2
No Solution
解析:
看到這高達3s的時間限制就知道這題不簡單……
題目大意:一個K位數A,只要A的各位之和(記為m)與A+1的各位之和(記為n)的最大公約數是一個大於2的質數,就叫做forever numbers。現在給定K和m,求出所有滿足條件的A。
所以就是一道大雜燴,既要求最大公約數,又要判斷質數,最后還要DFS+剪枝。剪枝的時候考慮:
1、A的最后一位一定是9,如果不是的話無法產生進位,m和n相差1,最大公約數就是1了。進一步考慮,A的最后兩位只可能是99。為什么呢?假設A的最后兩位是09,則A+1最后兩位為10,所以m和n相差8。對於兩個相差為8的數,最大公約數只能是1、2、4、8,(為什么3不是最大公約數呢?因為3至多只會是其中一個的約數,其他的數同理)而這幾個數都不滿足題意。同理,A的最后兩位是19、29...89都不滿足題意,所以A的最后兩位只可能是99。再考慮最后三位可不可能是999,假設最后三位是099,則A+1最后三位為100,m和n相差17,17是有可能成為m和n的最大公約數的,滿足條件。因此往這個方向的思路就此中斷,只能得到最后兩位一定是99。
2、感謝@陌上樓頭 提供思路。假設A的末位連續的9有nineNum個,A+1使得這nineNum個9全部變成0,並且前面那個數因進位而+1,因此n與m滿足n=m-nineNum×9+1。現在題目給了m,我們只需要遍歷nineNum的所有可能值就可以求得n,而不必對所有n進行遍歷。接下來求nineNum的范圍:如前所述,最小為2。至於最大值,首先它一定小於A的位數K(如果等於,也就是K個數字全部是9,+1進位成K+1位數10…0對應n=1,顯然是不滿足題意的),其次觀察n=m-nineNum×9+1,且n至少是2,得到m-nineNum×9+1=n ≥ 2,移項得到nineNum ≤ (m-1)/9,因此nineNum的最大值為min{K-1, ⌊(m-1)/9⌋}。
3、當前已確定的數的總和不能過大也不能過小。
枚舉的時候,先讓nineNum從大到小枚舉以唯一確定n(式子中nineNum越大n越小),再對A從首位開始枚舉,這樣得到的答案一定已經有序。
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
int N, K, m;
vector<int> A;
bool flag = false; // 沒有答案為false
bool isPrime (int x) {
// 本來2是質數,這里為配合題意讓2也不是質數
if (x <= 2) return false;
int sqr = (int)sqrt(x);
for (int i = 2; i <= sqr; i++) {
if (x % i == 0) return false;
}
return true;
}
int gcd (int a, int b) {
return b != 0 ? gcd(b, a % b) : a;
}
// 把A拆成2個部分,第二部分就是nineNum個9,剩下的就是第一部分,長為part1Num
int nineNum, n, part1Num;
// m - nineNum * 9就是第一部分各位數之和,eamin是第一部分的未確定位的數之和,index是A當前要確定的位的下標
void DFS (int remain, int index) {
// 剪枝:remain太大了
if ((part1Num - index) * 9 - 1 < remain) return;
// 邊界:到第一部分的最后一個數
if (index == part1Num - 1) {
if (remain == 9) return;
if (isPrime(gcd(m, n))) {
flag = true;
A[index] = remain;
printf("%d ", n);
for (int i = 0; i < K; i++) {
// 形成的序列一定是按n, A的順序排列
printf("%d", i <= index ? A[i] : 9);
}
printf("\n");
}
return;
}
// 從0到9枚舉(首位不能是0)
for (int i = index == 0 ? 1 : 0; i <= 9; i++) {
// 不能枚舉比remain還大的數
if (i > remain) return;
A[index] = i;
DFS(remain - i, index + 1);
}
}
void test() {
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
scanf("%d %d", &K, &m);
printf("Case %d\n", i);
flag = false;
A.clear();
A.resize(K);
// 根據nineNum的值從首位開始一位一位確定數字
for (nineNum = min(K, m / 9); nineNum >= 2; nineNum--) {
n = m - nineNum * 9 + 1;
part1Num = K - nineNum;
DFS(m - nineNum * 9, 0);
}
if (!flag) {
printf("No Solution\n");
}
}
}
int main () {
test();
return 0;
}
7-2 Merging Linked Lists (25 分)
時間限制:400 ms 內存限制:64 MB
Given two singly linked lists L1=a1→a2→⋯→an−1→an and L2=b1→b2→⋯→bm−1→bm. If n≥2m, you are supposed to reverse and merge the shorter one into the longer one to obtain a list like a1→a2→bm→a3→a4→bm−1⋯. For example, given one list being 6→7 and the other one 1→2→3→4→5, you must output 1→2→7→3→4→6→5.
Input Specification:
Each input file contains one test case. For each case, the first line contains the two addresses of the first nodes of L1 and L2, plus a positive N (≤105) which is the total number of nodes given. 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 a positive integer no more than 105, and Next is the position of the next node. It is guaranteed that no list is empty, and the longer list is at least twice as long as the shorter one.
Output Specification:
For each case, output in order the resulting linked list. Each node occupies a line, and is printed in the same format as in the input.
Sample Input:
00100 01000 7
02233 2 34891
00100 6 00001
34891 3 10086
01000 1 02233
00033 5 -1
10086 4 00033
00001 7 -1
Sample Output:
01000 1 02233
02233 2 00001
00001 7 34891
34891 3 10086
10086 4 00100
00100 6 00033
00033 5 -1
解析:
靜態鏈表題,按模板來就好了。先在鏈表的結構體里加一個index參數並初始化為一個很大的數,再對兩個鏈表按要求進行遍歷, 同時修改遍歷到的結點的index,最后將所有結點按index排序。
要確定兩個鏈表誰長誰短,所以得先遍歷一遍確定長度。又由於合並的時候短鏈表要倒過來遍歷,干脆在求長度的時候把兩個鏈表的地址都放到vector里。輸出地址記得用%05d,但是-1要單獨輸出。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 100000;
typedef struct Node {
int data, address, next, index = 0x3fffffff;
} Node;
Node nodes[maxn];
bool cmp (Node a, Node b) {
return a.index < b.index;
}
vector<int> list1, list2; // 分別存儲L1和L2的各節點的下標
// 遍歷鏈表得到長度,並把各節點存進數組方便合並
int get_len(int root, vector<int> &list) {
while (root != -1) {
list.push_back(root);
root = nodes[root].next;
}
return list.size();
}
// 合並兩個數組並修改index的值,其中link1是較長的,link2是較短的
void set_index (int max_len, int min_len, vector<int> &link1, vector<int> &link2) {
int a = 0;
int b = min_len - 1;
int count = 0;
for (; b >= 0; b--) {
nodes[link1[a]].index = count++; a++;
nodes[link1[a]].index = count++; a++;
nodes[link2[b]].index = count++;
}
// 處理長鏈表剩下的結點
for (; a < max_len; a++) {
nodes[link1[a]].index = count++;
}
}
void test() {
int l1, l2, n, t1, t2, t3;
int i;
scanf("%d %d %d", &l1, &l2, &n);
for (i = 0; i < n; i++) {
scanf("%d %d %d", &t1, &t2, &t3);
nodes[t1].address = t1;
nodes[t1].data = t2;
nodes[t1].next = t3;
}
int len1 = get_len(l1, list1), len2 = get_len(l2, list2);
if (len1 > len2) {
set_index(len1, len2, list1, list2);
} else {
set_index(len2, len1, list2, list1);
}
sort(nodes, nodes + maxn, cmp);
for (i = 0; i < len1 + len2 - 1; i++) {
printf("%05d %d %05d\n", nodes[i].address, nodes[i].data, nodes[i+1].address);
}
printf("%05d %d -1", nodes[i].address, nodes[i].data);
}
int main() {
test();
return 0;
}
7-3 Postfix Expression (25 分)
時間限制:400 ms 內存限制:64 MB
Given a syntax tree (binary), you are supposed to output the corresponding postfix expression, with parentheses reflecting the precedences of the operators.
Input Specification:
Each input file contains one test case. For each case, the first line gives a positive integer N (≤ 20) which is the total number of nodes in the syntax tree. Then N lines follow, each gives the information of a node (the i-th line corresponds to the i-th node) in the format:
data left_child right_child
where data is a string of no more than 10 characters, left_child and right_child are the indices of this node's left and right children, respectively. The nodes are indexed from 1 to N. The NULL link is represented by −1. The figures 1 and 2 correspond to the samples 1 and 2, respectively.
![]() |
![]() |
|---|---|
| Figure 1 | Figure 2 |
Output Specification:
For each case, print in a line the postfix expression, with parentheses reflecting the precedences of the operators.There must be no space between any symbols.
Sample Input 1:
8
* 8 7
a -1 -1
* 4 1
+ 2 5
b -1 -1
d -1 -1
- -1 6
c -1 -1
Sample Output 1:
(((a)(b)+)((c)(-(d))*)*)
Sample Input 2:
8
2.35 -1 -1
* 6 1
- -1 4
% 7 8
+ 2 3
a -1 -1
str -1 -1
871 -1 -1
Sample Output 2:
(((a)(2.35)*)(-((str)(871)%))+)
解析:
先建靜態二叉樹,再后序遍歷。進入DFS加左括號,退出本次DFS加右括號。需要判斷左子樹是否存在,如果存在,運算符要放到后面;如果不存在,運算符放到前面。根的確定方法:維護一個哈希表,凡是子節點就將對應項置1,最后掃一遍哈希表,值為0的就是根。
注意,測試點4把+當做正號使用,不能只特判-。
#include <string>
#include <iostream>
using namespace std;
typedef struct Node {
string data;
int l, r;
} Node;
Node nodes[22];
int isRoot[22];
void post (int index) {
if (index == -1) return;
printf("(");
if (nodes[index].l == -1) {
cout << nodes[index].data;
post(nodes[index].r);
} else {
post(nodes[index].l);
post(nodes[index].r);
cout << nodes[index].data;
}
printf(")");
}
void test() {
int N, left, right;
int i, root;
string s;
scanf("%d", &N);
for (i = 1; i <= N; i++) {
cin >> s >> left >> right;
nodes[i].data = s;
nodes[i].l = left; isRoot[left] = 1;
nodes[i].r = right; isRoot[right] = 1;
}
for (i = 1; i <= N; i++) if (isRoot[i] == 0) break;
root = i;
post(root);
}
int main() {
test();
return 0;
}
7-4 Dijkstra Sequence (30 分)
時間限制:1500 ms 內存限制:64 MB
Dijkstra's algorithm is one of the very famous greedy algorithms. It is used for solving the single source shortest path problem which gives the shortest paths from one particular source vertex to all the other vertices of the given graph. It was conceived by computer scientist Edsger W. Dijkstra in 1956 and published three years later.
In this algorithm, a set contains vertices included in shortest path tree is maintained. During each step, we find one vertex which is not yet included and has a minimum distance from the source, and collect it into the set. Hence step by step an ordered sequence of vertices, let's call it Dijkstra sequence, is generated by Dijkstra's algorithm.
On the other hand, for a given graph, there could be more than one Dijkstra sequence. For example, both { 5, 1, 3, 4, 2 } and { 5, 3, 1, 2, 4 } are Dijkstra sequences for the graph, where 5 is the source. Your job is to check whether a given sequence is Dijkstra sequence or not.
Input Specification:
Each input file contains one test case. For each case, the first line contains two positive integers Nv (≤103) and Ne (≤105), which are the total numbers of vertices and edges, respectively. Hence the vertices are numbered from 1 to Nv.
Then Ne lines follow, each describes an edge by giving the indices of the vertices at the two ends, followed by a positive integer weight (≤100) of the edge. It is guaranteed that the given graph is connected.
Finally the number of queries, K, is given as a positive integer no larger than 100, followed by K lines of sequences, each contains a permutationof the Nv vertices. It is assumed that the first vertex is the source for each sequence.
All the inputs in a line are separated by a space.
Output Specification:
For each of the K sequences, print in a line Yes if it is a Dijkstra sequence, or No if not.
Sample Input:
5 7
1 2 2
1 5 1
2 3 1
2 4 1
2 5 2
3 5 1
3 4 1
4
5 1 3 4 2
5 3 1 2 4
2 3 4 5 1
3 2 1 5 4
Sample Output:
Yes
Yes
Yes
No
解析:
要判斷迪傑斯特拉序列,也就是每一輪循環中凡是路徑最小的點都可以選,而不是按照模板所有路徑最小的點中選序號最小的。為此用unordered_set存儲所有路徑最小的點,每次松弛前檢查下序列對應點是否在里面。
#include <unordered_set>
#include <string>
#include <vector>
using namespace std;
const int maxn = 1002, INF = 0x3fffffff;
typedef struct Node {
int next, dist;
} Node;
vector<Node> G[maxn];
int dist[maxn], visited[maxn];
int nv, ne, K;
vector<int> input;
bool dij () {
int i, j, min_dist, cur_index;
for (i = 1; i <= nv; i++) { dist[i] = INF; visited[i] = 0; }
dist[input[0]] = 0;
for (i = 0; i < nv; i++) {
min_dist = INF - 1;
unordered_set<int> temp;
// 先找到最小值
for (j = 1; j <= nv; j++) {
if (!visited[j] && min_dist > dist[j]) {
min_dist = dist[j];
}
}
// 再把所有最小路徑加入unordered_set
for (j = 1; j <= nv; j++) {
if (!visited[j] && min_dist == dist[j]) {
temp.insert(j);
}
}
if (temp.count(input[i]) == 0) return false;
// 松弛
cur_index = input[i];
visited[cur_index] = 1;
for (auto &node : G[cur_index]) {
if (!visited[node.next] && dist[cur_index] + node.dist < dist[node.next]) {
dist[node.next] = dist[cur_index] + node.dist;
}
}
}
return true;
}
void test() {
int i, j, t1, t2, t3;
scanf("%d %d", &nv, &ne);
input.resize(nv);
for (i = 0; i < ne; i++) {
scanf("%d %d %d", &t1, &t2, &t3);
Node node; node.next = t2, node.dist = t3;
Node node2; node2.next = t1, node2.dist = t3;
G[t1].push_back(node); G[t2].push_back(node2);
}
scanf("%d", &K);
for (i = 0; i < K; i++) {
for (j = 0; j < nv; j++) scanf("%d", &input[j]);
printf("%s\n", dij() ? "Yes" : "No");
}
}
int main() {
test();
return 0;
}
總結
| 編號 | 標題 | 分數 | 類型 |
|---|---|---|---|
| 7-1 | Forever | 20 | 5.2 最大公約數+5.4 素數+8.1 DFS |
| 7-2 | Merging Linked Lists | 25 | 7.3 鏈表處理 |
| 7-3 | Postfix Expression | 25 | 9.2 二叉樹的遍歷 |
| 7-4 | Dijkstra Sequence | 30 | 10.4 最短路徑 |
究極縫合怪他來了,帶着第一題的下馬威來了!第一題最難沒有問題吧,這題恰似一記當頭棒喝,打得所有人(大佬除外)七葷八素,時間打沒了,心態也打沒了。第二題是套路題的老熟人,稍微變通一下就好。第三題也是套路題,套了個應用的殼,根據應用進行變通。第四題看懂了題就知道,只需把迪傑斯特拉算法的模板稍作改動。所以就是一道難題+三道套路題,第一題:我不是針對誰,我是說在座各題,都是垃圾……


