PAT甲级_2020冬(97)

总结

第9次模考97分。1、3题简单题。第4题有意思,需要灵活使用stl容器。剩下两个多小时写第二题也没有AC,开始以为是动态,最后发现是自己想复杂了,直接遍历1E8的时间复杂度居然没被卡TLE。

第一题

5min

题意

给定一个正整数n,找到和n最接近的斐波那契数。

思路

  • 通过非递归方法挨个求数 F i F_i Fi
  • F i F_i Fi插入ans数列队尾。
  • 同时比较 F i F_i Fi和n的大小关系,大于等于n时跳出循环。
  • 然后比较ans数组中最后一个元素比n大多少,倒数第二个元素比n小多少。
  • 按要求输出。

小结

  • 开始ans从1开始存而非0,导致第一次没AC(n = 1的情况)。

题解

#include<bits/stdc++.h>
using namespace std;
vector<int> ans;
int main(){
	int a = 0, b = 1, n, tmp = 1; cin>>n;
	ans.push_back(a), ans.push_back(b);
	while(ans.back() < n){
		tmp = a + b;
		ans.push_back(tmp);
		a = b, b = tmp;
	}
	printf("%d", tmp - n >= n - ans[ans.size() - 2] ? ans[ans.size() - 2] : ans.back());
	return 0;
}

第二题

22/25

题意

给出两个字符串s和p,s长度10000。p一定是s的子串。注意这里子串定义:p[i + 1]一定在p[i]在s中匹配位置的后面,但不一定连续
求p在s中最短的匹配串。

思路

暴力即可。

小结

300ms没卡1E8时间复杂度的时间是我没想到的。题解40ms,我的错解3用时7ms。

题解

#include<bits/stdc++.h>
using namespace std;
int ptr, len = INT_MAX;
int main(){
	string s, p, ans;
	getline(cin, s), getline(cin, p);
	for(int i = 0; i < s.size(); i ++){
		if(s[i] == p[0]){
			int j = i + 1, k = 1;
			for(; j < s.size() && k < p.size(); j ++)
				if(s[j] == p[k]) k ++;
			if(k == p.size()){
				if(j - i + 1 < len){
					len = j - i + 1;
					ptr = i;
				}
			}
		}
	}
	cout<<s.substr(ptr, len - 1);
	return 0;
}

错解1 - DP - MLE(18)

思路:

  • 建立一个表f[10010][10010];横轴下标1开始对应s的每一个元素,纵轴下标1开始对应p的每一个元素。f[i][j]记录s取子串s[1, j],p取子串p[1, i]的匹配起点,具体如下:
  • p[1]开始,遍历所有s下标,如果s[j] == p[1]f[1][j] = j, 否则f[1][j] = f[1][j - 1]。j就是p与s匹配的起点。
  • 然后遍历2 - p.size()if(p[i] == s[j]) f[i][j] = f[i - 1][j - 1];else f[i][j] = f[i][j - 1];
  • 容易证明最后一排f[p.size() - 1][j] !=0 表示此时整段p与s前j个元素能匹配上了,并且匹配起点为f[p.size() - 1][j]
  • 找到最小长度len = j - f[l2 - 1][j]ans = s.substr(f[p.size() - 1][j], len + 1);即可。
    问题:
  • 1个short大小2B,1E8个short大小是200MB。测试点3、5、6MLE
#include<bits/stdc++.h>
using namespace std;
short f[10010][10010];
int main(){
	string s, p, ans;
	getline(cin, s), getline(cin, p);
	s = "0" + s, p = "0" + p;
	int l1 = s.size(), l2 = p.size();
	for(int j = 1; j < l1; j ++){
		if(p[1] == s[j]) f[1][j] = j;
		else f[1][j] = f[1][j - 1];
	}
	for(int i = 2; i < l2; i ++){
		for(int j = i; j < l1; j ++){
			if(p[i] == s[j]) f[i][j] = f[i - 1][j - 1];
			else f[i][j] = f[i][j - 1];
		}
	}
	int len = INT_MAX;
	for(int j = 1; j < l1; j ++){
		if(f[l2 - 1][j] && j - f[l2 - 1][j] >= 0 && j - f[l2 - 1][j] < len){
			len = j - f[l2 - 1][j];
			ans = s.substr(f[p.size() - 1][j], len + 1);
		}
	}
	if(ans.size() > 1000)return 0;//???
	cout<<ans<<endl;
	return 0;
}

错解2 - DP滚动数组(18)

MLE了,思考滚动数组,因为数组只需要该行和前一行数据。这里要着重记录一下,因为没被下模板,只能从头推。虽然没有MLE。但测试点1、2、3、4WA。暂时没分析出来原因。

#include<bits/stdc++.h>
using namespace std;
short f[3][100010];
int main(){
	string s, p, ans;
	getline(cin, s), getline(cin, p);
	s = "0" + s; p = "0" + p;
	for(int i = 1; i < s.size(); i ++){
		if(s[i] == p[1]) f[1][i] = i;
		else f[1][i] = f[1][i - 1];
	}
	int ptr = 1, pre = 0;
	for(int i = 2; i < p.size(); i ++){
		ptr = ptr % 2 + 1, pre = pre % 2 + 1;
		for(int j = i; j < s.size(); j ++){
			if(s[j] == p[i]) f[ptr][j] = f[pre][j - 1];
			else f[ptr][j] = f[ptr][j - 1];
		}
	}
	int len = INT_MAX;
	for(int i = p.size(); i < s.size(); i ++){
		if(f[ptr][i] > 0 && i - f[ptr][i] < len){
			len = i - f[ptr][i];
			ans = s.substr(f[ptr][i], len + 1);
		}
	}
	cout<<ans;
	return 0;
}

错解3 - DP - STL优化(22)

  • deque<pair<int, int>>rec进一步优化空间,每次判断也只用找上一段匹配成功的情况。
  • 这里在第二层while跳出循环前的判断语句应该是有问题的,应该break。
#include<bits/stdc++.h>
using namespace std;
short f[3][100010];
char pc;
int main(){
	string s, p, ans;
	getline(cin, s), getline(cin, p);
	deque<pair<int, int>> rec, rec2;//first是匹配的开始位置,second是匹配的终止位置 
	for(int i = 0; i < s.size(); i ++)
		if(s[i] == p[0]) rec.push_back({i, i});//从i开始 
	int ptr = 1, e = s.size() - p.size() + 1;
	pair<int, int> tar;
	while(ptr != p.size()){//ptr每次指向下一个匹配元素p[ptr]; 取值从1到p.size() - 1; 
		pc = p[ptr];//取待匹配元素到pc 
		rec2.clear();//rec的缓存 
		while(rec.size()){//访问rec的每一个元素 
			bool flag = false;
			for(int i = rec.front().second + 1; i < e + ptr; i ++){//取目标rec.front()匹配位置的下一个位置,到s.size() - p.size() + 1 + ptr; 
				if(s[i] == pc){//当匹配成功,说明rec中front以及之后的元素中second小于i的都可以与之匹配。 
					while(rec.size() && rec.front().second < i){
						tar = rec.front();
						rec.pop_front();
					}//出循环时要么rec空了,要么当前rec第一个元素终止位置大于等于i了 
					rec2.push_back({tar.first, i});//最近与i匹配的存入rec2. 
					flag = true;
					break;
				}
			}
			if(flag == false) rec.pop_front();
			//flag == false 说明rec中元素当前匹配都失败了。应该break的,而非pop_front()。 
		}
		rec = rec2;
		ptr ++;
	}
	tar = {0, 10001010};
	for(auto & i : rec)
		if(i.second - i.first < tar.second - tar.first) tar = i;//找起始点和终点差距最小的元素。 
	cout<<s.substr(tar.first, tar.second - tar.first + 1);
}

第三题

14min

题意

给出文件数n,后续n行给出文件目录深度和文件名。然后给出询问次数k,每次给出文件名,要求如果文件存在则输出路径,如果不存在则输出不存在。

思路

  • 用数组p记录文件名a的上级目录,数组pre记录最近一次出现在第i层的文件目录名,st表示文件名是否存在。
  • 循环n次。
    • 依次处理得到文件深度cnt(该行前面空格数)和文件名stoi(str)
    • 记录文件名存在st[stoi(str)] = true;,记录该文件名在pre[cnt],建立该文件与上层文件的映射关系p[stoi(str)] = pre[cnt - 1];
  • 循环k次。
    • 每次输入目标文件名到t,首先判断t是否存在。
    • 然后如果存在,通过ans数组,从t遍历到根节点。
    • 按要求输出。

小结

  • str.substr(cnt) : 从下标cnt到末尾。

题解 - 优化待测试

#include<bits/stdc++.h>
using namespace std;
int n, k, t, pre[1005], p[10010], st[10001];
int main(){
	cin>>n>>t;
	string str;
	pre[0] = t;
	st[t] = true;
	getchar();
	for(int i = 1; i < n; i ++){
		getline(cin, str);
		int cnt = 0;
		while(str[cnt] == ' ') cnt++;
		t = stoi(str.substr(cnt));
		st[t] = true;
		p[t] = pre[cnt - 1];
		pre[cnt] = t;
	}
	cin>>k;
	while(k--){
		cin>>t;
		if(!st[t]) printf("Error: %04d is not found.\n", t);
		else{
			deque<int> ans;
			while(t != 0){
				ans.push_front(t);
				t = p[t];
			}
			ans.push_front(0);
			for(int i = 0; i < ans.size(); i ++)
				printf("%04d%s", ans[i], i == ans.size() - 1 ? "\n": "->");			
		}
	}
	return 0;
}

第四题

33min

题意

给出反应物数量n,然后依次给出n个反应物编号00-99
给出目标产物数量m,然后依次给出产物编号00-99
给出化学式k,然后依次给出k个化学方程式,左边是反应物,右边是产物。
最后要求输出得到所有产物的一种可能。
注意:反应物也可以直接当产物用。同时如果有两种方式合成该产物,则输出较小的那种方法。

思路

  • 用数组rea记录反应物,数组st记录编号反应物是否有被用过。数组pro记录产物,如果产物存在于反应物中,则记录生产的一种可能于rec。
  • 然后循环k次分析每个化学式。tmp记录反应物编号,tar记录产物编号,存在map<int, set<deque<int>>> rec;中。解释:rec[i]表示编号为i的所有生产方式组合,set用于按题目要求对组合从小到大排序。
  • 然后进行DFS§,每次合成下标p的产物,如果p == m则合成完毕,记录输出。因为要最小的,所以得到第一个满足要求的式子就可以剪枝后面全部不遍历了。
    • 注意DFS中,每次for(auto i : rec[tar])i是所有能合成tar反应式的deque,先检查i中元素是否全部存在,如果存在,则使用for(auto j : i) st[j] = false;并记录tmp.push_back(i);,进入下一层,出来时tmp.pop_back();,恢复其存在for(auto j : i) st[j] = true;

小结

  • 这题很有意思,造了几个特殊的数据结构比如map<int, set<deque<int>>>; vector<deque<int>>

题解

#include<bits/stdc++.h>
using namespace std;
int n, m, k, rea[30], pro[20];
bool st[101];
string str;
map<int, set<deque<int>>> rec;
vector<deque<int>> ans, tmp;
void DFS(int p){
	if(p == m || ans.size()){
		if(ans.size() == 0) ans = tmp;
		return;
	}
	int tar = pro[p];
	for(auto i : rec[tar]){
		bool f = true;
		for(auto j : i)
			if(!st[j]) f = false;
		if(!f) continue;
		tmp.push_back(i);
		for(auto j : i) st[j] = false;
		DFS(p + 1);
		for(auto j : i) st[j] = true;
		tmp.pop_back();
	}
}
int main(){
	cin>>n;
	for(int i = 0; i < n; i ++){
		cin>>rea[i];
		st[rea[i]] = true;
	}
	cin>>m;
	for(int i = 0; i < m; i ++){
		cin>>pro[i];
		st[pro[i]] = true;
		deque<int>tmp;
		tmp.push_back(pro[i]);
		rec[pro[i]].insert(tmp);
	}
	cin>>k;
	getchar();
	for(int i = 0; i < k; i ++){
		getline(cin, str);
		deque<int> tmp;
		while(str.find('+') != -1){
			tmp.push_back(stoi(str.substr(0, str.find('+') - 1)));
			str.erase(0, str.find('+') + 2);
		}
		tmp.push_back(stoi(str.substr(0, str.find(' '))));
		str.erase(0, str.find('>') + 1);
		int tar = stoi(str);
		rec[tar].insert(tmp);
	}
	DFS(0);
	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", pro[i]);
	}
	return 0;
}

7-1 The Closest Fibonacci Number

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.

7-2 Subsequence in Substring

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 atpaaabpabttpabt 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

7-3 File Path

FP.JPG

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.

7-4 Chemical Equation

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​+2H2​O 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 H2​O.

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 and 01 + 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), but 01 + 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值