7-1 The Closest Fibonacci Number (20 分)
時間限制:150 ms 內存限制:64 MB
The Fibonacci sequence Fn is defined by Fn+2=Fn+1+Fn for n≥0, with F0=0 and F1=1. The closest Fibonacci number is defined as the Fibonacci number with the smallest absolute difference with the given integer N.
Your job is to find the closest Fibonacci number for any given N.
Input Specification:
Each input file contains one test case, which gives a positive integer N (≤108).
Output Specification:
For each case, print the closest Fibonacci number. If the solution is not unique, output the smallest one.
Sample Input:
305
Sample Output:
233
Hint:
Since part of the sequence is { 0, 1, 1, 2, 3, 5, 8, 12, 21, 34, 55, 89, 144, 233, 377, 610, ... }, there are two solutions: 233 and 377, both have the smallest distance 72 to 305. The smaller one must be printed out.
解析:
方法一:一個接一個算,直到算出來的大於N為止,然后判斷根據哪種情況輸出。
#include <vector>
#include <iostream>
using namespace std;
vector<int> fa; // 存Fibonacci序列
void test () {
int N;
scanf("%d", &N);
if (N == 1) {
printf("1");
return;
}
fa.push_back(0);
fa.push_back(1);
fa.push_back(1);
while (fa.back() < N) {
fa.push_back(fa.back() + fa[fa.size() - 2]);
}
int temp = fa[fa.size() - 2];
if (fa.back() == N) printf("%d", fa.back());
else if (fa.back() - N < N - temp) printf("%d", fa.back());
else printf("%d", temp);
}
int main() {
test();
return 0;
}
方法二:直接在本地算出所有小於等於108的斐波那契數,打表。
#include <iostream>
using namespace std;
int fa[41] = {0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986,102334155};
/* 本地試探出10^8以內斐波那契數的最大下標
int fab (int index) {
if (index <= m) return fa[index];
int ans = fab(index - 1) + fab(index - 2);
fa[index] = ans;
return ans;
}
*/
void test() {
int i, N;
scanf("%d", &N);
for (i = 1; i <= 40; i++) {
if (N <= fa[i]) break;
}
if (N == fa[i]) printf("%d", N);
else {
if (N - fa[i - 1] <= fa[i] - N) printf("%d", fa[i - 1]);
else printf("%d", fa[i]);
}
}
int main() {
test();
return 0;
}
7-2 Subsequence in Substring (25 分)
時間限制:300 ms 內存限制:64 MB
A substring is a continuous part of a string. A subsequence is the part of a string that might be continuous or not but the order of the elements is maintained. For example, given the string atpaaabpabtt
, pabt
is a substring, while pat
is a subsequence.
Now given a string S and a subsequence P, you are supposed to find the shortest substring of S that contains P. If such a solution is not unique, output the left most one.
Input Specification:
Each input file contains one test case which consists of two lines. The first line contains S and the second line P. S is non-empty and consists of no more than 104 lower English letters. P is guaranteed to be a non-empty subsequence of S.
Output Specification:
For each case, print the shortest substring of S that contains P. If such a solution is not unique, output the left most one.
Sample Input:
atpaaabpabttpcat
pat
Sample Output:
pabt
解析:
暴力查找,一個指針指向S,一個指針指向P。只要在S中找到等於P[0]的字符,就開始找以該字符起始的最小子串。遞歸或者非遞歸都可以。注意:不要用string的find方法,很慢。
方法一:DFS+剪枝。
#include <iostream>
using namespace std;
string S, P;
int min_start = 0, min_length;
void DFS (int j, int i, int s_start) { // j指向P,i指向S,s_start表示當前查找的S的子串的開始下標
if (j == P.length()) {
if (min_length > i - s_start) {
min_length = i - s_start;
min_start = s_start;
}
return;
}
// 剪枝:S已掃完而P未掃完,或者當前的S子串已經和記錄的最小長度相當了
if (i >= S.length() || i - s_start >= min_length) return;
// 在S中找下一個等於P[j]的字符
while (i < S.length() && S[i] != P[j]) i++;
if (i < S.length()) {
DFS(j + 1, i + 1, s_start);
}
}
void test () {
cin >> S >> P;
int start;
min_length = S.length();
// 只要在S中找到等於P[0]的字符,就開始遞歸
for (start = 0; start + P.length() < S.length(); start++) {
if (S[start] == P[0]) DFS(1, start + 1, start);
}
printf("%s", S.substr(min_start, min_length).c_str());
}
int main() {
test();
return 0;
}
方法二:非遞歸。
#include <iostream>
using namespace std;
string S, P;
int min_start, min_length;
void test () {
int i, j, start;
cin >> S >> P;
min_length = S.length();
for (start = 0; start + P.length() <= S.length(); start++) {
// 只要在S中找到等於P[0]的字符,就開始找以該字符起始的最小子串
if (S[start] == P[0]) {
for (j = 1, i = start + 1; j < P.length() && i < S.length(); j++, i++) {
while (P[j] != S[i] && i < S.length()) i++;
}
if (j == P.length() && min_length > (i - start)) {
min_length = i - start;
min_start = start;
}
}
}
printf("%s", S.substr(min_start, min_length).c_str());
}
int main() {
test();
return 0;
}
7-3 File Path (25 分)
時間限制:400 ms 內存限制:64 MB
The figure shows the tree view of directories in Windows File Explorer. When a file is selected, there is a file path shown in the above navigation bar. Now given a tree view of directories, your job is to print the file path for any selected file.
Input Specification:
Each input file contains one test case. For each case, the first line gives a positive integer N (≤103), which is the total number of directories and files. Then N lines follow, each gives the unique 4-digit ID of a file or a directory, starting from the unique root ID 0000
. The format is that the files of depth d will have their IDs indented by d spaces. It is guaranteed that there is no conflict in this tree structure.
Then a positive integer K (≤100) is given, followed by K queries of IDs.
Output Specification:
For each queried ID
, print in a line the corresponding path from the root to the file in the format: 0000->ID1->ID2->...->ID
. If the ID
is not in the tree, print Error: ID is not found.
instead.
Sample Input:
14
0000
1234
2234
3234
4234
4235
2333
5234
6234
7234
9999
0001
8234
0002
4 9999 8234 0002 6666
Sample Output:
0000->1234->2234->6234->7234->9999
0000->1234->0001->8234
0000->0002
Error: 6666 is not found.
解析:
1、輸入各層的文件時,用棧存儲各父級目錄以及層數,且棧頂為當前目錄。每次輸入一個新文件時,根據縮進判斷該文件的層數,再對比棧頂文件的層數:
如果該文件層數=棧頂文件層數+1,當前目錄不變;
如果該文件層數=棧頂文件層數+2,說明要再深一層,把上次輸入的文件壓入棧;
剩余的情況都是該文件層數≤棧頂文件層數,那就不斷出棧直到該文件層數=棧頂文件層數+1為止,即找到了正確的當前目錄。
2、各層的文件形成一棵樹,在查找各個文件的路徑時,既可以從根開始,也可以從該文件向上找到根。對應的,可以建立不同的數據結構。
如果從根開始,就在結點的結構體里開個vector存放各孩子的結點,用DFS或BFS遍歷。如果倒過來,結構體里就存放自己的父親。
方法一:結構體里存放各孩子,自頂向下使用DFS。標准的DFS要遍歷所有結點,故增加一個flag,只要找到了路徑就可以退出DFS。
#include <vector>
#include <unordered_set>
#include <stack>
#include <iostream>
using namespace std;
typedef struct Node {
int id, layer;
vector<Node*> children;
} Node;
stack<Node*> stack1;
int N, target;
Node* root = NULL;
unordered_set<int> nodes; // 保存所有結點的ID,方便判斷ID是否存在
vector<int> path; // 保存路徑
int flag = 0; // 用於DFS中快速退出,因為不必遍歷所有結點
void DFS (Node *node) {
// 當flag為1時,提前結束遍歷
if (flag == 1) return;
path.push_back(node->id);
// 遞歸邊界
if (node->id == target) {
for (int i = 0; i < path.size(); i++) {
printf("%04d", path[i]);
if (i < path.size() - 1) printf("->");
}
printf("\n");
flag = 1;
return;
}
for (auto item : node->children) {
DFS(item);
}
path.pop_back();
}
void test () {
int i, j, K;
string input;
scanf("%d\n", &N);
// 單獨處理根結點
root = new Node;
root->id = 0; root->layer = 0;
stack1.push(root);
nodes.insert(0);
getline(cin, input);
for (i = 1; i < N; i++) {
getline(cin, input);
// 確定層數
for (j = 0; j < input.size(); j++) {
if (input[j] != ' ') break;
}
int id = stoi(input.substr(j, 4));
nodes.insert(id);
if (j == stack1.top()->layer + 2) {
stack1.push(stack1.top()->children.back());
} else if (j <= stack1.top()->layer) {
while (stack1.top()->layer >= j) stack1.pop();
}
// 把當前結點放入父節點的數據結構中
Node *node = new Node;
node->layer = j; node->id = id;
stack1.top()->children.push_back(node);
}
scanf("%d", &K);
for (i = 0; i < K; i++) {
scanf("%d", &target);
if (nodes.count(target) == 0) printf("Error: %04d is not found.\n", target);
else {
path.clear();
flag = 0;
DFS(root);
}
}
}
int main() {
test();
return 0;
}
方法二:從下往上找路徑,每個結點結構體里存放父節點的ID。再用一個字典存放所有結點。
#include <stack>
#include <vector>
#include <map>
#include <iostream>
using namespace std;
typedef struct Node {
int layer;
string father;
} Node;
map<string, Node> parents; // 存儲整棵樹
stack<string> stack1; // 棧里存放父節點的ID
int N, K;
vector<string> ans; // 存儲路徑(逆向)
void test() {
int i, j;
string input, last, temp, root; // last存放上次輸入的結點的ID
scanf("%d\n", &N);
getline(cin, input);
// 單獨處理根結點
root = input;
Node node;
node.layer = 0, node.father = "";
parents[input] = node;
stack1.push(input);
last = input;
for (i = 1; i < N; i++) {
getline(cin, input);
j = 0;
// 確定層數
while (input[j] == ' ') j++;
input = input.substr(j, 4);
int top_layer = parents[stack1.top()].layer;
if (j == top_layer + 2) {
stack1.push(last);
} else if (j <= top_layer) {
do {
stack1.pop();
top_layer = parents[stack1.top()].layer;
} while (top_layer + 1 != j);
}
// 把父節點的ID放入當前節點的數據結構中
Node node1;
node1.layer = j;
node1.father = stack1.top();
parents[input] = node1;
// last存放上次輸入的結點的ID
last = input;
}
scanf("%d", &K);
for (i = 0; i < K; i++) {
cin >> input;
if (parents.count(input) == 1) {
ans.clear();
temp = input;
while (temp != root) {
ans.push_back(temp); temp = parents[temp].father;
}
printf("%s", root.c_str());
for (j = (int)ans.size() - 1; j >= 0; j--) printf("->%s", ans[j].c_str());
printf("\n");
} else {
printf("Error: %s is not found.\n", input.c_str());
}
}
}
int main() {
test();
return 0;
}
7-4 Chemical Equation (30 分)
時間限制:400 ms 內存限制:64 MB
A chemical equation is the symbolic representation of a chemical reaction in the form of symbols and formulae, wherein the reactant entities are given on the left-hand side and the product entities on the right-hand side. For example, CH4+2O2=CO2+2H2O means that the reactants in this chemical reaction are methane and oxygen: CH4 and O2, and the products of this reaction are carbon dioxide and water: CO2 and H2O.
Given a set of reactants and products, you are supposed to tell that in which way we can obtain these products, provided that each reactant can be used only once. For the sake of simplicity, we will consider all the entities on the right-hand side of the equation as one single product.
Input Specification:
Each input file contains one test case. For each case, the first line gives an integer N (2≤N≤20), followed by N distinct indices of reactants. The second line gives an integer M (1≤M≤10), followed by M distinct indices of products. The index of an entity is a 2-digit number.
Then a positive integer K (≤50) is given, followed by K lines of equations, in the format:
reactant_1 + reactant_2 + ... + reactant_n -> product
where all the reactants are distinct and are in increasing order of their indices.
Note: It is guaranteed that
- one set of reactants will not produce two or more different products, i.e. situation like
01 + 02 -> 03
and01 + 02 -> 04
is impossible; - a reactant cannot be its product unless it is the only one on the left-hand side, i.e.
01 -> 01
is always true (no matter the equation is given or not), but01 + 02 -> 01
is impossible; and - there are never more than 5 different ways of obtaining a product given in the equations list.
Output Specification:
For each case, print the equations that use the given reactants to obtain all the given products. Note that each reactant can be used only once.
Each equation occupies a line, in the same format as we see in the inputs. The equations must be print in the same order as the products given in the input. For each product in order, if the solution is not unique, always print the one with the smallest sequence of reactants -- A sequence { a1,⋯,am } is said to be smaller than another sequence { b1,⋯,bn } if there exists 1≤i≤min(m,n) so that aj=bj for all j<i, and ai<bi.
It is guaranteed that at least one solution exists.
Sample Input:
8 09 05 03 04 02 01 16 10
3 08 03 04
6
03 + 09 -> 08
02 + 08 -> 04
02 + 04 -> 03
01 + 05 -> 03
01 + 09 + 16 -> 03
02 + 03 + 05 -> 08
Sample Output:
02 + 03 + 05 -> 08
01 + 09 + 16 -> 03
04 -> 04
解析:
大模擬題,應該也沒啥別的辦法,就用DFS慢慢找咯。
1、用unordered_set存放原料,vector存放要求的產物
2、既然每個方程式只有一個產物,用三維數組存放每個產物的原料,方便反查。
vector<vector<int>> input[100];
一維放各元素的ID,二維表示各元素可以通過哪些方式得到,三維則是各個方程式的原料。例如,參考樣例,08有三種方式可以得到:
03 + 09 -> 08 ①
02 + 03 + 05 -> 08 ②
08 -> 08 ③
那么按如下方式存放:
input[8][0] = {3, 9};
input[8][1] = {2, 3, 5};
input[8][2] = {8};
盡管題目中按順序①式在②式前面,但是為了得到符合題目要求(選擇原料序列最小的方案)的結果,需要進行排序:(vector本身是可以進行比較的)
sort(input[8].begin(), input[8].end());
這樣就得到了如下存放順序:
input[8][0] = {2, 3, 5};
input[8][1] = {3, 9};
input[8][2] = {8};
3、產物不必作為之后反應的原料。如果把產物放進原料里,可能會報錯。
代碼:
#include <vector>
#include <unordered_set>
#include <algorithm>
#include <iostream>
using namespace std;
vector<vector<int>> input[100];
int N, M, K;
vector<int> b; // 存放要求的產物
unordered_set<int> a; // 存放原料
int flag1 = 0;
vector<int> ans[10]; // 存放結果
void DFS (int product_index) {
// 如果flag1為1,結束DFS
if (flag1 == 1) return;
// 遞歸邊界,輸出結果,結束所有DFS
if (product_index >= M) {
flag1 = 1;
for (int i = 0; i < M; i++) {
for (int j = 0; j < ans[i].size(); j++) {
printf("%02d %s ", ans[i][j], j < ans[i].size() - 1 ? "+" : "->");
}
printf("%02d\n", b[i]);
}
return;
}
int product = b[product_index];
int flag = 0;
// 根據產物反查每種方程式
for (auto& reactants : input[product]) {
flag = 0;
// 對比當前方程式所需原料,判斷所給原料能否提供
for (auto reactant : reactants) {
if (a.count(reactant) == 0) {
flag = 1;
break;
}
}
// 如果所給原料可以滿足需求,將方程式記入ans,然后進行下一步DFS(反查下一個產物)
if (flag == 0) {
ans[product_index] = reactants;
// 從所給原料里刪除所需原料
for (auto reactant : reactants) {
a.erase(reactant);
}
DFS(product_index + 1);
// 恢復所需原料
for (auto item : reactants) {
a.insert(item);
}
}
}
}
void test () {
int i, t1;
string t2;
scanf("%d", &N);
for (i = 0; i < N; i++) {
scanf("%d", &t1);
a.insert(t1);
// 建立諸如 01 -> 01 的方程式
vector<int> temp(1, t1);
input[t1].push_back(temp);
}
scanf("%d", &M);
b.resize(M);
for (i = 0; i < M; i++) scanf("%d", &b[i]);
scanf("%d", &K);
// 存儲方程式
for (i = 0; i < K; i++) {
vector<int> reactants;
// 讀入原料
while (1) {
scanf("%d", &t1);
cin >> t2;
reactants.push_back(t1);
if (t2 != "+") break;
}
// 讀入產物,並對原料排序
scanf("%d", &t1);
sort(reactants.begin(), reactants.end());
input[t1].push_back(reactants);
}
// 所有方程式存儲完后,對各個方程按原料序列從小到大排序
for (auto item : b) {
sort(input[item].begin(), input[item].end());
}
// 從第一個要求產物開始DFS
DFS(0);
}
int main() {
test();
return 0;
}
總結
編號 | 標題 | 分數 | 類型 |
---|---|---|---|
7-1 | The Closest Fibonacci Number | 20 | 5.1 簡單數學 |
7-2 | Subsequence in Substring | 25 | 3.6 字符串處理 |
7-3 | File Path | 25 | 7.1 棧的應用 |
7-4 | Chemical Equation | 30 | 8.1 DFS |
按通過率來看,最后一題最難,其次是第二題。第一題簡單,打表也能做。第二題字符串處理,用比較暴力的辦法也能做出來。第三題考簡單邏輯,不難想。第四題一個是要想到DFS,另一個要處理好數據,我感覺挺麻煩的。根據小伙伴們的反應,有的迅速AK完,有的最后兩小時就一直卡在最后一題上了,真就旱的旱死澇的澇死(而我則屬於菜死的)。PS:第二、四題都可以用DFS,第三題也能部分用DFS,這真的沒問題嗎……