云计算百科
云计算领域专业知识百科平台

PTA团体程序设计天梯赛L2真题讲解L2-021-024

官网https://pintia.cn/problem-sets/994805046380707840/exam/problems/type/7

文章目录

      • L2-021 点赞狂魔
      • L2-022 重排链表
      • L2-023 图着色问题
      • L2-024 部落

L2-021 点赞狂魔

题目大意:统计每个用户点赞的不同标签数量,选出数量最多的前3名作为“点赞狂魔”。若不同标签数量并列,则选择标签总个数更少(标签出现次数平均值更小)的用户。人数不足3时用-补齐。

解题思路:

  • 对每个用户,使用set容器对点赞标签自动去重,得到不同标签的数量,同时记录用户点赞的标签总个数。
  • 自定义排序规则:优先按不同标签数量降序排列;数量相同时,按标签总个数升序排列(对应平均值更小)。
  • 排序后取前3名输出,不足3人时用-补全。
  • 正解代码

    #include <bits/stdc++.h>
    //#define int long long
    using namespace std;
    const int N=110;
    int n;
    string s;
    struct no{
    string id;
    int num,ave;
    bool operator <(const no n1)const{
    if(num!=n1.num)return num>n1.num;
    return ave<n1.ave;
    }
    }a[N];
    signed main(){
    cin>>n;
    for(int i=0;i<n;i++){
    cin>>a[i].id;
    int k;
    cin>>k;
    set<int>s;
    for(int j=0;j<k;j++){
    int y;
    cin>>y;
    s.insert(y);
    }
    a[i].num=s.size();
    a[i].ave=k;
    }
    sort(a,a+n);
    int mm=min(3,n);
    for(int i=0;i<mm;i++){

    cout<<a[i].id;
    if(i!=2)cout<<' ';
    }
    if(n<3)for(int i=0;i<3n;i++){

    cout<<"-";
    if(i!=2n)cout<<' ';
    }

    return 0;
    }

    代码解析:

    • 定义结构体存储用户名、不同标签数num、标签总数ave(变量名实际表示总个数,用于间接比较平均值)。
    • 重载<运算符实现自定义排序,保证排序后符合题目优先级。
    • 输入时通过set去重,s.size()即为不同标签数量。
    • 输出部分先输出前min(3,n)个用户名,再根据总人数补全-,同时控制行末无多余空格。

    小技巧:

    • 平均值 = 总标签数 / 不同标签数,当不同标签数相等时,总个数越小平均值越小,因此直接比较总个数即可,无需计算浮点数,避免精度问题。

    L2-022 重排链表

    题目大意:给定一个单链表 L₁→L₂→…→Lₙ,将其重新排列为 Lₙ→L₁→Lₙ₋₁→L₂→… 的形式,按指定格式输出重排后的链表。

    解题思路:

  • 链表线性化:先根据头结点地址遍历链表,将所有节点按原顺序存入数组,得到有序的节点序列,同时过滤掉不在链表上的无效节点。
  • 双指针重构:使用左指针指向链表头部,右指针指向链表尾部,交替从尾部、头部取节点,拼接成新的链表顺序。
  • 格式化输出:每个节点地址按5位补零输出,最后一个节点的后继地址为-1。
  • 正解代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+9;
    struct node{
    int id,x,next;
    }l[N],ans[N];
    map<int,int>mp;
    int main(){
    int head,n;
    cin>>head>>n;
    for(int i=0;i<n;i++){
    cin>>l[i].id>>l[i].x>>l[i].next;
    mp[l[i].id]=i;
    }
    int now=mp[head],count=0;
    for(int i=0;i<n;i++){
    ans[i]=l[now];
    count++;
    if(l[now].next==1)break;
    now=mp[l[now].next];
    }
    int le=0,ri=count1;
    int cnt=0;
    printf("%05d %d ",ans[ri].id,ans[ri].x);
    ri;
    while(le<=ri){
    if(cnt&1){
    printf("%05d\\n%05d %d ",ans[ri].id,ans[ri].id,ans[ri].x);
    ri;
    cnt++;
    }
    else{
    printf("%05d\\n%05d %d ",ans[le].id,ans[le].id,ans[le].x);
    le++;
    cnt++;
    }
    }
    cout<<1;

    return 0;
    }

    代码解析:

    • 用数组存储所有输入节点,通过map建立「地址→数组下标」的映射,方便快速通过地址定位节点。
    • 从头结点开始遍历,将链表按顺序存入结果数组,同时统计有效节点总数。
    • 双指针交替取节点,先取最右侧节点,再依次取左、右节点,完成重排。
    • 使用printf("%05d")实现地址的5位补零格式,满足输出要求。

    L2-023 图着色问题

    题目大意:给定无向图和颜色数k,判断多组颜色分配方案是否为合法的图着色解:要求所有相邻顶点颜色不同,且恰好使用k种颜色。

    解题思路:

  • 直接存储图的所有边,无需构建邻接表,保存每条边的两个端点即可。
  • 对每组颜色方案:
    • 统计使用的不同颜色数量,若不等于k则直接判定为不合法。
    • 遍历所有边,检查边的两个端点颜色是否相同,若存在相同则判定为不合法。
  • 根据检查结果输出Yes或No。
  • 正解代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=521;
    int n,m,k,a[N],t;
    bool fd=0;
    vector<pair<int,int>>g;
    int main(){
    cin>>n>>m>>t;
    for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    g.push_back({u,v});
    }
    cin>>k;
    while(k){
    map<int,int>mp;
    for(int i=1;i<=n;i++){
    cin>>a[i];
    mp[a[i]]++;
    }
    fd=0;
    for(auto [u,v]:g){
    if(a[u]==a[v]){fd=1;break;}
    }
    if((int)mp.size()!=t)fd=1;
    if(fd)cout<<"No";
    else cout<<"Yes";
    cout<<'\\n';
    }

    return 0;
    }

    代码解析:

    • 用vector<pair<int,int>>存储所有边,单次检查的时间复杂度为O(m),适配题目数据范围。
    • 用map或set统计颜色种类数,校验是否恰好等于给定的颜色数。
    • 用标记变量记录是否出现非法情况,一旦发现冲突可提前跳出循环,优化效率。

    L2-024 部落

    题目大意:给定多个小圈子,定义“朋友的朋友属于同一个部落”。要求统计社区总人数、互不相交的部落数量,并查询多对人员是否属于同一个部落。

    解题思路:

    • 标准并查集应用题,利用并查集维护人员的连通关系。
  • 初始化并查集,每个元素的父节点指向自己。
  • 对每个小圈子,将圈子内的所有人合并到同一个集合中。
  • 用集合统计所有出现过的人员编号,得到总人数;再统计所有人员的根节点去重后的数量,即为部落个数。
  • 对每个查询,判断两个人的根节点是否相同,输出对应结果。
  • 正解代码

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=1e4+9;
    int p[N],t,x,q,k,n;
    set<int>s1;
    set<int>s2;
    int find(int x){
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
    }
    signed main(){
    cin>>n;
    for(int i=1;i<=N;i++)p[i]=i;
    for(int i=0;i<n;i++){
    int k,fi,y;
    cin>>k>>fi;
    s1.insert(fi);
    int f1=find(fi);
    for(int i=1;i<k;i++){
    cin>>y;
    int f2=find(y);
    p[f2]=f1;
    s1.insert(y);
    }
    }
    cin>>q;
    for(auto x:s1)s2.insert(find(x));
    cout<<s1.size()<<' '<<s2.size()<<'\\n';
    while(q){
    int a,b;
    cin>>a>>b;
    int f1=find(a);
    int f2=find(b);
    if(f1==f2)cout<<"Y\\n";
    else cout<<"N\\n";

    }

    return 0;
    }

    代码解析:

    • find函数实现路径压缩,大幅优化查询效率。
    • 合并策略:将每个小圈子的后续成员都合并到第一个成员所在的集合中。
    • 用第一个set存储所有出现过的人,其大小即为总人数;遍历该集合将所有根节点存入第二个set,其大小即为部落数量。
    • 查询时调用两次find比较根节点,相同则输出Y,否则输出N。
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » PTA团体程序设计天梯赛L2真题讲解L2-021-024
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!