PAT笔记
基础语法补充
cout位数控制
c o u t cout cout通过 < i o m a n i p > <iomanip> <iomanip>包下的 s e t p r e c i s i o n ( x ) setprecision(x) setprecision(x)即可转换成输出x位数据,含整数小数一起。
*补充:precision设置后的输出会自动四舍五入!
例程如下:
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
double num;
cin>>num;
cout<<setprecision(3)<<num<<endl;
}
效果如下:
而在 s e t p r e c i s i o n setprecision setprecision前加上同为 < i o m a n i p > <iomanip> <iomanip>包下的 f i x e d fixed fixed关键字即可将输出类型锁定为浮点型,从而将 s e t p r e c i s i o n ( x ) setprecision(x) setprecision(x)中的 x x x固定到对小数点后位数的控制完成预期效果!
例程如下:
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
double num;
cin>>num;
cout<<fixed<<setprecision(3)<<num<<endl;
}
效果如下:
可能为空的字符串输入!!!
PTA-1033 旧键盘打字
解题思路非常清晰,使用 m a p map map进行哈希查找即可完成。
坑点在于题目只保证第二行输入非空,即第一行输入可能为空,此时使用cin是无法正常读入的。
必须使用 g e t l i n e ( c i n , a ) getline(cin, a) getline(cin,a)其中 a a a为任一字符串变量。
这样就可以将空行读入为空字符串""!!!
修正后的题解如下:
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
map<char, int> bad_dic;
string s1, s2;
getline(cin, s1);
cin>>s2;
string s = "";
if(s1==""){
cout<<s2;
return 0;
}
for(auto c:s1){
bad_dic[c]=1;
}
for(auto c:s2){
char temp = c;
if('a'<=c&&c<='z'){
temp=c+'A'-'a';
}
if(('A'<=c&&c<='Z')&&(bad_dic.count('+'))){
continue;
}
if(!bad_dic.count(temp)){
s+=c;
}
}
cout<<s;
}
带空格的字符串读入
PTA-1044 火星数字
关键在于当一行中输入的字符串带空格且需要多行读入时,此时应采用下列模板进行输入:
int N; cin>>N;
/**
* This line was a key line
* It's like a input-wakeup code here
* We will get an empty line without this code
*/
getchar();
for(auto i = 0; i < N; i++){
string str;
getline(cin, str);
}
最大公约数gcd
PTA-1034 有理数四则运算
当涉及要求公约数的数字较大时,cmath库中的gcd函数则需要进行覆写如下:
long long gcd(long long t1, long long t2) {
return t2 == 0 ? t1 : gcd(t2, t1 % t2);
}
否则会出现溢出的情况!!!
规则化输入scanf妙用!
PTA-1034 有理数四则运算
当输入的数据在有效数字间参杂其他字符如本题的:
num1/num2 num3/num4
此时若采用字符串处理会平添 O ( n ) O(n) O(n)的时间复杂度,并且soti只能转成int不能转成long long,还存在溢出情况,因此可以使用 < c s t d i o > <cstdio> <cstdio>库下的 s c a n f scanf scanf函数进行规则化输入如下:
scanf("%lld/%lld %lld/%lld",&a,&b,&c,&d);
即scanf的输入数据已占位符表示,可以灵活进行输入!!!
而cin无法进行这样的选择性/规则化输入。
cin单行输入句子存为字符串
PTA-1042 字符统计
当输入数据格式为在一行之内给出一个句子字符串时,不能简单地使用cin对单个字符串变量进行读取!!!
错误例程如下:
string s;
cin>>s;
正确作法:
string s = "";
while(cin.peek()!='\n'){
char c; cin>>c;
s += c;
// If you need do some operations on single input character like statistic
/* f() here refers to the operation you want to do on c */
f(c);
}
细说浮点数
PTA-1049 数列的片段和
细说浮点数
cout中的三目运算符
PTA-1051 复数乘法
在coding时常常需要在一个cout中进行带条件的输出,如果全用if-else来实现的话,代码不免过于冗长,而解决if-else冗长的一个常见工具则是三目运算符(condition)?result1:result2
e.g. 给定一个正整数N,随后N行输入N个整数,请将其符号和其一同输出,结尾不应有空行,样例如下:
输入数据:
3
-1
0
1
输出数据
-1
0
+1
if-else写法:
#include<iostream>
#include<vector>
using namespace std;
vector<int> input;
int main(){
int N; cin>>N;
for(int i = 0; i < N; i++){
int temp; cin>>temp;
input.push_back(temp);
}
for(int i = 0; i < N-1; i++){
if(input[i]>0){
cout<<'+';
}
cout<<input[i]<<endl;
}
if(input[N-1]>0){
cout<<'+';
}
cout<<input[N-1];
}
三目运算符写法:
#include<iostream>
#include<vector>
using namespace std;
vector<int> input;
int main(){
int N; cin>>N;
for(int i = 0; i < N; i++){
int temp; cin>>temp;
input.push_back(temp);
}
for(int i = 0; i < N-1; i++){
cout<<(input[i]>0?"+":"")<<input[i]<<endl;
}
cout<<(input[N-1]>0?"+":"")<<input[N-1];
}
*特别注意:
1)cout中待输出的三目表达式应被括号包裹住!
2)要表征某一条件下不需输出时,需要用字符串“”表示,使用字符‘’则会报错!(因为无空字符)
My Own 巧妙题解
PTA-1035 插入排序与归并排序
源码如下:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<vector<int>> insertion;
vector<vector<int>> merge_array;
void insertion_sort(vector<int> array){
for(int i = 0; i < array.size(); i++){
vector<int> temp = array;
sort(temp.begin(), temp.begin()+i+1);
insertion.push_back(temp);
}
return;
}
void merge_sort(vector<int> array){
int unit = 2;
int len = array.size();
while(unit<=len){
vector<int> temp = array;
int i = 0;
while(i+unit<=len){
sort(temp.begin()+i, temp.begin()+i+unit);
i+=unit;
}
sort(temp.begin()+i, temp.end());
merge_array.push_back(temp);
unit += unit;
}
sort(array.begin(), array.end());
merge_array.push_back(array);
return;
}
int main(){
int N;
cin>>N;
vector<int> input;
vector<int> mid;
for(int i = 0; i < N; i++){
int ori;
cin>>ori;
input.push_back(ori);
}
for(int i = 0; i < N; i++){
int mi;
cin>>mi;
mid.push_back(mi);
}
insertion_sort(input);
merge_sort(input);
// insert
// insertion[0] with no operation
for(int i = 1; i < N; i++){
if(mid==insertion[i]){
cout<<"Insertion Sort"<<endl;
for(int j = 0; j < N-1; j++){
cout<<insertion[i+1][j]<<" ";
}
cout<<insertion[i+1][N-1];
return 0;
}
}
// merge
for(int i = 0; i < merge_array.size(); i++){
if(mid==merge_array[i]){
cout<<"Merge Sort"<<endl;
for(int j = 0; j < N-1; j++){
cout<<merge_array[i+1][j]<<" ";
}
cout<<merge_array[i+1][N-1];
return 0;
}
}
}
核心思路:模拟——使用 < a l g o r i t h m > <algorithm> <algorithm>库下的 s o r t sort sort函数进行排序模拟来完成对每一次排序得到的序列进行记录。
插入排序模拟如下:
思路:通过对前 i i i个元素进行快排来模拟完成插入。
进一步优化——将 t e m p temp temp等效于前次操作的结果即 i n s e r t i o n . b a c k ( ) insertion.back() insertion.back()来缩短快排操作次数。
*注意:插入排序的第0次操作等同于位操作,遍历结果序列时应从1开始!!!
void insertion_sort(vector<int> array){
insertion.push_back(array);
for(int i = 1; i < array.size(); i++){
vector<int> temp = insertion.back();
sort(temp.begin(), temp.begin()+i+1);
insertion.push_back(temp);
}
return;
}
归并排序模拟如下:
思路:通过对每一个单元 u n i t unit unit进行自底向上的排序。
*注意:长度不足unit的剩余子序列也需要进行排序!!!
void merge_sort(vector<int> array){
int unit = 2;
int len = array.size();
while(unit<=len){
vector<int> temp = array;
int i = 0;
while(i+unit<=len){
sort(temp.begin()+i, temp.begin()+i+unit);
i+=unit;
}
sort(temp.begin()+i, temp.end());
merge_array.push_back(temp);
unit += unit;
}
sort(array.begin(), array.end());
merge_array.push_back(array);
return;
}
PTA-1060 爱丁顿数
源码如下:
#include<iostream>
#include<vector>
using namespace std;
int main(){
int N; cin>>N;
vector<int> miles(N+2, 0);
int E = 0;
for(int i = 0; i < N; i++){
int temp; cin>>temp;
miles[temp<=N?temp:N+1]++;
}
int summ = N;
for(int i = 0; i <= N; i++){
summ -= miles[i];
if(summ>=i){
E = i;
}
}
cout<<E;
}
是时间空间双 O ( n ) O(n) O(n)的线性模拟解法。
核心思路:由于一共骑车天数为 N N N,因此 E E E最大为 N N N。故以长度为 N + 2 N+2 N+2的容器记录骑车里数为 i i i的天数。接着从 0 0 0遍历到 N N N,总天数 N N N减去等于当前天数的里数的天数即为大于当前里数的总天数,若其大于等于当前天数,则爱丁顿数更新为之。
基础算法补充(带板子)
埃式筛
一种时间复杂度为 O ( n l o g n l o g n ) O(nlog nlog n) O(nlognlogn)的快速筛选出 2 − N 2-N 2−N中素数的筛法。
思路:通过遍历 2 − N 2-N 2−N,不断维护更新遇到的素数到容器中,同时将容器中每一个素数的倍数筛除。
板子如下:( N N N is a given integer)
// i-indexed element in isnp specifies whether i+1 was not a prime number(true)
vector<bool> isnp(N+1, false);
vector<int> primes;
for(int i = 2; i <= N; i++){
// i is a prime number
if(!isnp[i]){
primes.push_back(i);
// Start Sieve
for(int j = i+i; j <= N; j+= i){
isnp[j] = 1;
}
}
}
欧拉筛(Euler Sieve)
应用: PTA-1059 C语言竞赛
素数筛中由于非素数存在着多种因数,从而造成了同一非素数会被各个素因数筛去,这就是素数筛中的大量重复计算。若只以最小素因数筛去非素数,则得到了线性时间复杂度 O ( n ) O(n) O(n)的筛法——欧拉筛。
板子如下:( N N N is a given integer)
// i-indexed element in isnp specifies whether i+1 was not a prime number(true)
vector<bool> isnp(N+1, false);
vector<int> primes;
for(int i = 2; i <= N; i++){
// i is a prime number
if(!isnp[i]){
primes.push_back(i);
}
// Start sieve with primes vector
for(auto p:primes){
if(p*i>N){
break;
}
isnp[p*i]=1;
if(i%p == 0){
break;
}
}
}