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;
}
 
本题难度一般,主要的新东西在cin的操作函数ignore配合getline的使用
 

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,也就是确保加的除数不被抵消的最小数,同时也是确保在可被整除的情况下成功抵消的唯一数,这样就做到了向上取整。

 重点2在于循环时注意循环条件是否会随着循环的进行而改变,从而导致错误
这题的重点在于数学式子的构造;
 

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(){

  出口条件

  剪枝优化

  相邻递归

}

 
posted @ 2025-02-20 21:58  Elma-sorin  阅读(42)  评论(0)    收藏  举报