PTA刷题笔记
- 
L1-006 连续因子
#include<iostream> #include<cmath> using namespace std; bool isPrime(int N){ if(N==2) return true; int n=sqrt(N)+1; for(int i=2;i<=n;i++){ if(N%i==0) return false; } return true; } int main(){ int N; cin>>N; if(isPrime(N)){ cout<<"1\n"<<N<<endl; return 0; } int i; int factors[1000]; int count=0; for(i=2;i<=sqrt(N)+1;i++){ if(N%i==0){ factors[count]=i; count++; } } int min=1,minlen=1,index=0; int sum; for(i=0;i<count;i++){ sum=factors[i]; min=1; for(int k=i+1;k<count;k++){ if(factors[k]-factors[k-1]==1){ sum*=factors[k]; if(N%sum==0){ min++; if(min>minlen){ minlen=min; index=i; } } else{ break; } } else{ break; } } } cout<<minlen<<endl; for(i=0;i<minlen;i++){ if(i==minlen-1){ cout<<factors[i+index]<<endl; } else{ cout<<factors[i+index]<<"*"; } } return 0; }
重点在于
1、确保sum能被N整除,也就是确保因子乘积也是N的因子;
2、确保N的所有因子能被完全的包括到
3、采用双指针的方式逐步尝试遍历,避免漏项
4、注意在内外循环中的溢出问题。
- 
L1-009 N个数求和
#include<iostream> #include<string> using namespace std; long gcd(long i,long j){ if(j==0) return i; else return gcd(j,i%j);//错点6,拓展欧几里得求最大公因数 } void yuefen(long& i,long& j){ long disk=gcd(i,j); i/=disk; j/=disk; } class fs{ public: long fz; long fm; }; int main(){ int N; cin>>N; fs fens[N]; for(int i=0;i<N;i++){ char slash; cin>>fens[i].fz>>slash>>fens[i].fm;//错点4,使用char来跳过字符 } if(N==1){ cout<<fens[0].fz<<"/"<<fens[0].fm; return 0; } fs sum=fens[0];//错点3,累加掌握好技巧 for(int i=1;i<N;i++){ sum.fz=sum.fz*fens[i].fm+sum.fm*fens[i].fz; sum.fm=sum.fm*fens[i].fm; yuefen(sum.fz,sum.fm);//错点1,每次结束后约分,避免溢出 } long count=sum.fz/sum.fm; sum.fz%=sum.fm;//错点2,count的运算尽量依靠符号运算 yuefen(sum.fz,sum.fm); if(count==0&&sum.fz==0) cout<<0<<endl;//错点5,考虑情况要周到 else if(count==0) cout<<sum.fz<<"/"<<sum.fm<<endl; else if(sum.fz==0) cout<<count<<endl; else cout<<count<<" "<<sum.fz<<"/"<<sum.fm<<endl; return 0; }
错点在上面已经标注,下次一定注意
1、运算尽量用现成的符号
2、考虑情况要周到
3、累加,累乘,递归,指针运算一定要思路清晰
4、注意溢出问题,特别在循环和大数运算中
5、跳过字符的方法,字符串运算的技巧
6、一些常见函数的快速编写
- 
L1-011 A-B
#include<iostream> #include<string> using namespace std; int main(){ string A; string B; getline(cin,A); getline(cin,B); int i,k; for(i=0;i<B.length();i++){ for(k=A.length()-1;k>=0;k--){ if(B[i]==A[k]){ A.erase(k,1); } } } cout<<A<<endl; }
主要在于erase的使用
- 
L1-017 到底有多二
#include<iostream> #include<iomanip> #include<string> #include<algorithm> using namespace std; int main(){ string N; cin>>N; float sum=1; if(N[0]=='-'){ sum*=1.5; N.erase(0,1); } int n=count(N.begin(),N.end(),'2'); if((int)(N[N.length()-1]-'0')%2==0){ sum*=2; } sum=sum*n/N.length()*100; cout<<fixed<<setprecision(2)<<sum<<"%"<<endl; return 0; }
运用到
algorithm库中的count函数计算位数,和erase函数来删除
主要还是字符串的操作
需要注意的是setprecision()设置精度时,如果指定为小数后的精度设置,则一定要加上fixed,且带上iomanip头文件。
- 
L1-020 帅到没朋友
#include<iostream> #include<algorithm> #include<set> #include<queue> #include<iomanip> using namespace std; int main(){ int N; cin >> N; set<int> inhandsome; int i,j; for(i=0;i<N;i++){ int n; cin>>n; for(j=0;j<n;j++){ int ID; cin>>ID; if(n!=1){ inhandsome.insert(ID); } } } int M; set<int> CK; cin>>M; queue<int> contrast; for(i=0;i<M;i++){ int JY; cin>>JY; if(CK.find(JY)==CK.end()){ contrast.push(JY); } CK.insert(JY); } set<int> results; set_difference(CK.begin(),CK.end(),inhandsome.begin(),inhandsome.end(),inserter(results,results.begin())); if(results.empty()){ cout<<"No one is handsome"<<endl; } else{ bool isfirst=true; while(!contrast.empty()){ if(results.find(contrast.front())!=results.end()){ if(!isfirst) cout<<" "; cout<<fixed<<setw(5)<<setfill('0')<<contrast.front(); isfirst=false; } contrast.pop(); } cout<<endl; } return 0; }
几个新知识
一个是处理不重复的数据的时候可以用到标准库的集合,也就是set,set支持交并差集运算,是非常方便的,但是注意格式,运算存储时若对象是set,用inserter(results,results.begin()),若对象是vector,用back_inserter(results)即可
另外一个是for循环的快速遍历,即for(const int& id:results){
cout<<id<<" ";
}
即可快速遍历
这题还是比较有难度的;
- 
L1-025 正整数A+B
#include<iostream> #include<string> #include<cmath> using namespace std; int strtoint(string A){ int result=0; for(int i=0;i<A.length();i++){ int n=(int)(A[i]-'0'); result+=pow(10,A.length()-i-1)*n; } return result; } int main(){ string A; string B; string Get; getline(cin,Get);//重点,别遗忘。 int len=Get.length(); int i; for(i=0;i<len;i++){ if(Get[i]==' '){ break; } A.push_back(Get[i]);//字符串操作函数1 } i++; B=Get.substr(i);//字符串操作函数2 bool rA=true,rB=true; if(A.length()==0){ rA=false; } for(i=A.length()-1;i>=0;i--){ if(A[i]>'9'||A[i]<'0') rA=false; } for(i=B.length()-1;i>=0;i--){ if(B[i]>'9'||B[i]<'0') rB=false; } if(rA&&rB){ if(A.length()>=4) rA=false; if(B.length()>=4) rB=false; int a=strtoint(A),b=strtoint(B); if(a==1000) rA=true; if(b==1000) rB=true; if(a==0) rA=false; if(b==0) rB=false; } if(rA&&rB){ int a=strtoint(A),b=strtoint(B); int sum=a+b; cout<<A<<" + "<<B<<" = "<<sum<<endl; } else if(!rA&&rB){ cout<<"? + "<<B<<" = ?"<<endl; } else if(!rB&&rA){ cout<<A<<" + ? = ?"<<endl; } else{ cout<<"? + ? = ?"<<endl; } return 0; }
一个旧东西,getline的使用
一个新东西,substr字符串截取函数的使用,
这题注意的是整个的逻辑判断和读懂题目的意思,千万要小心这种题目。
- 
L1-027 出租
#include<iostream> #include<algorithm> #include<string> #include<set> using namespace std; int main(){ string s; cin>>s; set<char,greater<char>> arr;//新知识,关于sort排序的特殊使用和set序列的降序 int i; for(i=0;i<s.length();i++){ arr.insert(s[i]); } int a[11]; int index=0; for(i=0;i<11;i++){ index=0; for(const char& it : arr){ if(it==s[i]){ break; } index++; } a[i]=index; } bool isfirst=true;//特别输出标记 cout<<"int[] arr = new int[]{"; for(const char& it : arr){//迭代!好用 if(!isfirst) cout<<","; cout<<it; isfirst=false; } cout<<"};\nint[] index = new int[]{"; isfirst=true; for(i=0;i<11;i++){ if(!isfirst) cout<<","; cout<<a[i]; isfirst=false; } cout<<"};\n"; return 0; }
本题难度一般,一个新知识,即sort排序的降序,greater<>()的使用,以及set的降序定义
其他都是复习,如快速迭代器,STL容器使用,特殊输出的特殊标记等等
- 
L1-030 一帮一
#include<iostream> #include<string> using namespace std; class students{ public: bool isman; string name; bool ispeek=false; }; int main(){ int N; cin>>N; students Stu[50]; int i,j; for(i=0;i<N;i++){ cin>>Stu[i].isman>>Stu[i].name; } for(i=0;i<N/2;i++){ for(j=N-1;j>i;j--){ if(!Stu[j].ispeek&&Stu[i].isman!=Stu[j].isman){ cout<<Stu[i].name<<" "<<Stu[j].name<<endl; Stu[j].ispeek=true; break; } } } return 0; }
标记遍历法
L1-032 Left-pad
#include<iostream> #include<string> using namespace std; int main(){ int N; char c; cin>>N>>c; string s; cin.ignore();//重点1 getline(cin,s); if(N>s.length()){ for(int i=0;i<N-s.length();i++){ cout<<c; } cout<<s<<endl; } else if(N==s.length()){ cout<<s<<endl; } else{ for(int i=s.length()-N;i<s.length();i++){ cout<<s[i]; } cout<<endl; } return 0; }
L1-039 古风排版
#include<iostream> #include<string> using namespace std; int main(){ string s; int N; cin>>N; cin.ignore(); getline(cin,s); int i,j,Z; Z=(s.length() + N - 1) / N;//重点1 while(s.length()<N*Z){//重点2 s+=' '; } for(i=0;i<N;i++){ for(j=0;j<Z;j++){ cout<<s[N*(Z-j)-1-(N-i-1)]; } cout<<endl; } return 0; }
重点1在于向上取整的方法,即先加上除数使之溢出,再减去一个1,也就是确保加的除数不被抵消的最小数,同时也是确保在可被整除的情况下成功抵消的唯一数,这样就做到了向上取整。
L1-043 阅览室
#include<iostream> #include<cmath> using namespace std; class book{ public: bool istake=false; int start; }; int main(){ int N; cin>>N; int i; for(i=0;i<N;i++){ book Bok[1000]; int count=0; float sum=0; while(1){ int j; cin>>j; char st,slip; cin>>st; int shi,fen; cin>>shi>>slip>>fen; if(j==0) break; if(st=='S'){ Bok[j].istake=true; Bok[j].start=shi*60+fen; } else if(st=='E'&&Bok[j].istake==true){ Bok[j].istake=false; count++; sum+=shi*60+fen-Bok[j].start; } else continue; } if(count!=0) cout<<count<<" "<<round(sum/count)<<endl; else{ cout<<"0 0"<<endl; } } return 0; }
这题主要是逻辑问题,逻辑清楚后面就都清楚了
不要想太难,判断状态用一个bool类型就可以解决
L1-046 整除光棍
#include<iostream> #include<cmath> using namespace std; int main(){ long long x; cin>>x; long long l=1; int w=1; while(l<x){ l=l*10+1; w++; } while(1){ if(l/x>0){ cout<<l/x; l=l%x; } else if(l/x==0) cout<<0; if(l%x==0) break; l=l*10+1; w++; } cout<<" "<<w<<endl; return 0; }
这题采用模拟手除的方法,避免大数无法存储而采用逐个输出
L1-056 猜数字
#include<iostream> #include<string> #include<numeric> #include<iomanip> using namespace std; int main(){ int N; cin>>N; string s[10000]; float num[10000]; float sums; int i; for(i=0;i<N;i++){ cin>>s[i]>>num[i]; } sums=accumulate(num,num+N,0)/(N*2); float minnum=abs(num[0]-sums); string minstr=s[0]; for(i=0;i<N;i++){ if(minnum>abs(num[i]-sums)){ minstr=s[i]; minnum=abs(num[i]-sums); } } cout<<fixed<<setprecision(0)<<sums<<" "<<minstr<<endl; return 0; }
新知识是numeric库中的
accumulate函数,使用size计算数组长度,使用方法为accumulate(begin,end,start)
L1-059 敲笨钟
#include<iostream> #include<string> #include<algorithm> using namespace std; int main(){ int N; cin>>N; string s; int i; cin.ignore(); for(i=0;i<N;i++){ getline(cin,s); int pos=s.find(','); int index=s.find('.'); if((s.substr(0,pos)).length()<3||(s.substr(pos+1,index-pos)).length()<3){ cout<<"Skipped"<<endl; } else if(s.substr(pos-3,3)!="ong"||s.substr(index-3,3)!="ong"){ cout<<"Skipped"<<endl; } else{ for(int j=0;j<3;j++){ s=s.substr(0,s.rfind(' ')); } cout<<s<<" qiao ben zhong."<<endl; } } return 0; }
难点在于substr的运用,其中要考虑到当给定值小于3以至于substr函数错误的情况
L1-064 估值一亿的AI核心代码
#include<iostream> #include<string> using namespace std; string takethis(string s){ string c=""; bool isfront=true; for(int i=0;i<s.length();i++){ if(s[i]==' '&&isfront) continue; else if(s[i]==' '&&s[i+1]>='0'&&s[i+1]<='9') c+=s[i]; else if(s[i]==' '&&(!isalpha(s[i+1])||i==s.length()-1)) continue; else{ if(s[i]=='?') c+='!'; else if(isalpha(s[i])&&s[i]!='I') c+=tolower(s[i]); else c+=s[i]; isfront=false; } } for(int i=0;i<c.length();i++){ i=c.find("I",i);//find的遍历手段 if(i==-1) break; if(!isalpha(c[i-1])&&!isalpha(c[i+1])) c.replace(i,1,"yoU"); } for(int i=0;i<c.length();i++){ i=c.find("me",i);//find的遍历手段 if(i==-1) break; if(!isalpha(c[i-1])&&!isalpha(c[i+2])) c.replace(i,2,"yoU"); } for(int i=0;i<c.length();i++){ i=c.find("could you",i); if(i==-1) break; if(!isalpha(c[i-1])&&!isalpha(c[i+9])) c.replace(i,9,"I Could"); } for(int i=0;i<c.length();i++){ i=c.find("can you",i); if(i==-1) break; if(!isalpha(c[i-1])&&!isalpha(c[i+7])) c.replace(i,7,"I Can"); } return c; } int main(){ int N; cin>>N; cin.ignore(); for(int i=0;i<N;i++){ string s; getline(cin,s); cout<<s<<endl; string c; c=takethis(s); cout<<"AI: "; for(int j=0;j<c.length();j++){ if(c[j]=='U') cout<<"u"; else if(c[j]=='C') cout<<"c"; else cout<<c[j]; } cout<<endl; } return 0; }
这题主要是find函数的运用和遍历,以及对字符串的处理,非常非常麻烦,并且输入极其刁钻,非常容易出错
L1-094 剪切粘贴
#include<iostream> #include<string> using namespace std; int main(){ string s; cin>>s; int N; cin>>N; for(int i=0;i<N;i++){ int x,y; string slow,shigh; cin>>x>>y>>slow>>shigh; string str; str=s.substr(x-1,y-x+1); s.erase(x-1,y-x+1); int pos=s.find(slow+shigh);//小细节,联合查找可以避免错误 if(pos==-1) s+=str; else { s.insert(pos+slow.length(),str); } } cout<<s<<endl; return 0; }
这题目一个很重要是看题目,题目有两个很重要的要求,一个是找不到放最后,一个是找到很多的话插入前面的,满足第一个条件很简单,第二个条件可以用两个字符串加起来联合查找,这样就能保证插入的是第一个并且不会出错。
L1-095 分寝室
#include<iostream> #include<cmath> using namespace std; int main(){ int n0,n1,n; cin>>n0>>n1>>n; int min=pow(2,30);//min值一定要足够大才行 int ntomin=0; bool istake=false; if(n>(n0+n1)){ cout<<"No Solution"<<endl; return 0; } for(int i=1;i<n;i++){ if(n0%i!=0||n1%(n-i)!=0) continue; else if(n0<=i||n1<=(n-i)) continue; else{ if(abs(n0/i-n1/(n-i))<min){ min=abs(n0/i-n1/(n-i)); ntomin=i; istake=true; } } } if(!istake) cout<<"No Solution"<<endl; else{cout<<ntomin<<" "<<n-ntomin<<endl;} return 0; }
min值一定要足够大
L1-100 四项全能
#include<iostream> using namespace std; int main(){ int n,m; cin>>n>>m; int sums=0; for(int i=0;i<m;i++){ int x; cin>>x; sums+=x; } if(sums%n==0){ if(sums==m*n) cout<<n<<endl; else if(sums<=(m-1)*n) cout<<0<<endl; } else if(n>sums) cout<<0<<endl;//人很多但是技能点少 else cout<<sums%n<<endl; return 0; }
注意几种情况,1、人很多,技能点不够,说明有0个人掌握m种技能
2、技能点刚好是m*n,说明全部都掌握,
做题时一定要考虑两个极端,一个是都有,一个是都没有。
L2-1 盲盒包装流水线
#include <iostream> #include <vector> #include <stack> #include <queue> #include <unordered_map> using namespace std; int main() { int N, S; cin >> N >> S; vector<int> bianhao(N); // 直接存编号,避免 queue 额外开销 for (int i = 0; i < N; i++) { cin >> bianhao[i]; } unordered_map<int, int> hezi_map; // 用于快速查找 bianhao 对应的 leixing stack<int> lex; for (int i = 0; i < N / S; i++) { for (int j = 0; j < S; j++) { int lx; cin >> lx; lex.push(lx); } for (int j = 0; j < S; j++) { hezi_map[bianhao[i * S + j]] = lex.top(); // 直接存入 map lex.pop(); } } int K; cin >> K; while (K--) { int l; cin >> l; if (hezi_map.count(l)) { cout << hezi_map[l] << endl; } else { cout << "Wrong Number" << endl; } } return 0; }
新内容:unordered_map,这个会创建一个哈希查找的map,没有序列,但是查找极快,能达到O(1)的查找速度,使用方法是unordered_map<(int(例子)),(int)> name
初始化name[(int)] = (int)查找name[(int)]即可,
注意点:vec容器虽可以插入删除,但是速度较慢,优势在变长数组和索引,所以可以代替stack和queue(两个的pop和push都要时间)
专题:STL容器实现两种图的数据结构
1、邻接矩阵:使用vector<vector<int>> graph(n,vector<int>(n,INF));即可创建,随后可以用索引以及各种函数进行操作
2、邻接表:使用vector <int> graph[n]即可,可以用resize()函数动态调整边长
L2-002 龙龙送快递
#include<iostream> #include<queue> #include<vector> using namespace std; const int INF = 100001; int f[INF],d[INF]; vector<int> v[INF]; bool visit[INF]; void dfs(int u){ for(auto & it : v[u]){ d[it]=d[u]+1; dfs(it); } } int main(){ int N,M; cin>>N>>M; int root; for(int i=1;i<=N;i++){ cin>>f[i]; if(f[i]==-1) root=i; else v[f[i]].push_back(i); } dfs(root); int maxs=-1; int vv=0; priority_queue<int> pq; pq.push(maxs); for(int i=0;i<M;i++){ int x; cin>>x; pq.push(d[x]); while(!visit[x]&&x!=root){ visit[x]=true; vv+=2; x=f[x]; } cout<<vv-pq.top()<<endl; } return 0; }
动态规划的思想,用访问数组可以节省很多操作
每次取最大值可以直接用priority_queue
dfs的遍历思路
void dfs(){
出口条件
剪枝优化
相邻递归
}

 
                
             
         浙公网安备 33010602011771号
浙公网安备 33010602011771号