GPLT 团体程序设计天梯赛 2026 题解

📌 转载声明:
本文为原创文章,首发于个人博客 acm-phrolova.top
遵循 CC BY-NC 4.0 版权协议。转载或引用请附上原文出处链接及本声明。
原文链接: https://acm-lycoris.cn/posts/gplt%E5%9B%A2%E4%BD%93%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E5%A4%A9%E6%A2%AF%E8%B5%9B2026%E9%A2%98%E8%A7%A3/


GPLT 团体程序设计天梯赛 2026 题解


title: GPLT团体程序设计天梯赛2026题解
date: 2026-04-22 20:38:10
tags: [c++, 算法, 天梯赛]
categories: [GPLT天梯赛, 2026]

📝 比赛简介

GPLT 团体程序设计天梯赛 2026 全国总决赛

每道题的讲解结构:题目 → 思路 → 代码

📚 题目目录(点击展开 / 收起)

L1-1 一行代码

分数:5

🔖 本题小目录

题目描述

每次一行代码,建起我们的未来 —— 本题非常简单,就请你直接在屏幕上输出这句话:
“Building the Future, One Line of Code at a Time.”

输入格式

本题没有输入。

输出格式

在一行中输出 Building the Future, One Line of Code at a Time.

输入样例

输出样例

Building the Future, One Line of Code at a Time.

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

本题没有任何难点,直接输出指定字符串即可。

代码 (Python)

print('Building the Future, One Line of Code at a Time.')


L1-2 要刷多少题

分数:5

🔖 本题小目录

题目描述

老师说:想在天梯赛取得好成绩,第一件事是把前面 n 年的天梯赛真题做一遍。
已知每年的天梯赛有 15 道题目,全新不重复。请你算一下,一共要刷多少道真题?

输入格式

输入第一行给出一个正整数 n(≤10),为题面中所述老师提出的比赛年数。

输出格式

在一行中输出学生需要做完的题目总数

输入样例

2

输出样例

30

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

直接输出n*15

代码(c++)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;

int main() {
    int a;
    cin >> a;
    cout << a * 15 << endl;
    return 0;
}


L1-3 就挺突然的

分数:10

🔖 本题小目录

题目描述

在这里插入图片描述

上面是新浪微博上的一张图,内容是“蹲厕所时突然想到,2026 年出生的孩子,能活到 3001 年”,就,挺突然的……
本题假设人类最长寿命为 250 岁,请你编写程序判断一下墙上的标语是否合理。

输入格式

输入在一行给出 2 个不超过 5000 的正整数 A 和 B,对应的标语为“蹲厕所时突然想到,A 年出生的孩子,能活到 B 年”。

输出格式

首先在第一行输出写标语的人认为 A 年出生的孩子有多长的寿命。如果该寿命超过了人类最长寿命,在第二行中输出 jiu ting tu ran de…;如果该寿命不是正数,输出 hai sheng ma?;如果寿命在正常范围内,输出 nin tai cong ming le!

输入样例1

2026 3001

输出样例1

975
jiu ting tu ran de...

输入样例2

3001 2026

输出样例2

-975
hai sheng ma?

输入样例3

2025 2275

输出样例3

250
nin tai cong ming le!

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

计算寿命
标语说“A 年出生的孩子能活到 B 年”,那么这个人活了 B - A 年。

判断合理性

如果 B - A > 250 → 超过人类最长寿命,不合理 → 输出 jiu ting tu ran de…

如果 B - A <= 0 → 还没出生或出生即死,不合理 → 输出 hai sheng ma?

否则(1 <= 寿命 <= 250)→ 合理 → 输出 nin tai cong ming le!

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    int a,b;
    cin>>a>>b;
    int diff=b-a;
    cout<<diff<<endl;
    if(diff<=0){
        cout<<"hai sheng ma?"<<endl;
    }else{
        if(diff>250){
            cout<<"jiu ting tu ran de..."<<endl;
        }else{
            cout<<"nin tai cong ming le!"<<endl;
        }
    }
    
    return 0;
}

L1-4 普及赛排名

分数:10

🔖 本题小目录

题目描述

普及赛计算排名时,仅将高校评级分不低于 1700 分的高校学生总人数作为基数。本题给出所有参赛学生所属高校的评级分,储程序中间值,统计参与计算排名的学生总人数。

输入格式

输入第一行给出正整数 n(n<=10^4),为参赛学生人数。随后 n 行,每行给出一位参赛学生所属高校的评级分,为区间 [0,3000] 内的整数。

输出格式

在一行中输出高校评级分不低于 1700 分的高校学生总人数。

输入样例

5
1500
2700
1700
1000
1699

输出样例

3

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

定义一个计数器,小于1700就+1

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    int n;
    cin>>n;
    int res=0;
    while(n--){
        int x;
        cin>>x;
        if(x<1700){
            res++;
        }
    }
    
    cout<<res<<endl;
    return 0;
}

L1-5 做什么都被骂怎么办

分数:15

🔖 本题小目录

题目描述

求问:“我做什么都被骂怎么办?”
子曰:“那就意味着你什么都可以做。”
给定一系列人被夸或被骂的记录找出那些什么都可以做的人。

输入格式

输入第一行给出一个正整数 n(n<=10^4),是记录的条数。随后 n 行,每行按下列格式给出一个人的记录:

编号 记录
其中 编号 是 1 到 100000 的整数,记录 是 0 表示被骂,1 表示被夸。

输出格式

在一行中按升序输出那些什么都可以做(即做什么都被骂)的人的编号。数字间以 1 个空格分隔,行首尾不得有多余空格。
如果不存在这样的人,输出 NONE。

输入样例1

7
23333 0
1 0
2 0
1 1
2 0
666 1
5555 0

输出样例1

2 5555 23333

输入样例2

3
1 1
2 0
2 1

输出样例2

NONE

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

用一个字典(或哈希表)记录每个编号是否出现过“被夸”的记录。
初始时,所有编号都默认为“-1”。表示没出现
出现了就先令其为0,再加上编号值
最终只要是0就表示什么都可以做,加入答案数组即可,答案数组为空就表示NONE

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    int n;
    cin>>n;
    vector<int> arr(114514,-1);

    
    for(int i=0;i<n;i++)
    {
        int a,b;
        cin>>a>>b;
        if(arr[a]==-1){
            arr[a]=0;
            arr[a]+=b;
        }else{
            arr[a]+=b;
        }
    }
    
    bool Can=false;

    vector<int> res;
    for(int i=0;i<114514;i++){
        if(arr[i]==-1)continue;
        
        if(arr[i]==0){
            res.push_back(i);
        }

    }

    if(res.empty()){
        cout<<"NONE"<<endl;
        return 0;
    }else{
        int cnt=res.size();
        for(int i=0;i<cnt;i++){
            if(i)cout<<" ";
            cout<<res[i];
            
        }cout<<endl;
    }


    return 0;
}

L1-6 钓鱼佬专用挪车电话

分数:15

🔖 本题小目录

题目描述

在这里插入图片描述

上图是新浪微博一位网友发的“钓鱼佬专用挪车电话”,钓鱼佬将长短不一的鱼漂一溜排开插在板上,每支鱼漂用目数表示电话号码对应位置上的数字。
本题请你判断钓鱼佬的挪车电话到底是什么?

注: 请不要拨打这个电话号码

输入格式

输入分 11 行,每行对应 11 位手机号码的一位数字,给出由 m 组成的字符串,以回车结束。每个 m 代表鱼漂上的一目,一行中有多少 m 就表示这一位数字是多少,空行代表 0。题目保证每行都不超过 9 个 m。

输出格式

一行输出钓鱼佬的手机号

输入样例

m
mm
mmmmmmmm
mmm
m
mm
m
mmmmmmmm

mmmmmmmm
mm

输出样例

12831218082

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

本题难点在于对空行的处理,很多人会直接cin,然后WA一次
注:很多人绝对不包括我(
使用getline处理空行,getline会把换行符\n读进去,如果本行只有一个换行符就取0即可

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    vector<int> res;
    for(int i=0;i<11;i++){
        string s;
        getline(cin,s);
        if(s=="\n"){
            res.push_back(0);
        }else{
            res.push_back(s.length());
        }
        
    }

    for(int i=0;i<11;i++){
        cout<<res[i];
    }cout<<endl;

    return 0;
}

L1-7 网络流量监测

分数:20

🔖 本题小目录

题目描述

网安工程师需要分析服务器日志中的网络流量数据,找出潜在的攻击行为。
编写程序,分析给定时间段内的网络流量数据,找出流量最大值、最小值、平均值和中值,并且标出超过平均值 2 倍的疑似攻击点。

输入格式

输入第一行给出正整数 n(≤10^3),为流量数据的总条数。第二行给出 n 个不超过 10^6 的非负整数,依次对应每小时记录的进入流量(以 MB 为单位)。

输出格式

输出第一行依次给出流量的最大值、最小值、平均值(向下取整。第二行升序输出所有疑似攻击点的小时数(从 1 到 n)。如果没有疑似攻击点,则输出 Normal。

同行数据间以 1 个空格分隔,行首尾不得有多余空格。

输入样例1

10
150 180 200 120 5000 136 115 131 4700 239

输出样例1

5000 115 1097
5 9

输入样例2

10
150 180 200 120 50 136 115 131 47 239

输出样例2

239 47 136
Normal

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

题目很明白了

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    int n;
    cin>>n;
    vector<int> arr(n);
    int Sum=0;
    for(int i=0;i<n;i++){
        cin>>arr[i];
        Sum+=arr[i];
    }

    cout<<*max_element(arr.begin(),arr.end())<<" ";
    cout<<*min_element(arr.begin(),arr.end())<<" ";
    cout<<Sum/n<<endl;

    int dan=Sum/n*2;
    vector<int> res;
    for(int i=0;i<n;i++){
        if(arr[i]>dan){
            res.push_back(i+1);
        }
    }
    if(res.empty()){
        cout<<"Normal"<<endl;
        return 0;
    }else{

        for(int i=0;i<res.size();i++){
            if(i)cout<<" ";
            cout<<res[i];
        }cout<<endl;

    }

    return 0;
}

L1-8 智慧文本编辑器

分数:20

🔖 本题小目录

题目描述

众所周知,随着基于大语言模型(LLM)的人工智能的大规模普及,现在越来越多的系统拥有了人工智能模块(当然,拼题 A 也有)。
为了响应潮流,龙龙打算也做一个智慧文本编辑器,但因为大语言模型的 API 太贵了,龙龙打算让这个编辑器的“智慧”停留在名字上就好了。但功能还是得写的,具体来说,对于当前正在编辑的文档,这个编辑器应当支持以下三个功能:

1 查找指定字符串 s1 前 3 次出现的位置;
2 在指定位置 p 插入一个指定字符串 s2
3 将某一段连续的字符串翻转。

真的文本编辑器可太复杂了,这里我们只简单化考虑由大小写英文字母和数字组成的字符串。

输入格式

输入第一行是一个整数 N (1≤N≤50),表示操作的数量。

第二行是一个字符串 S (1≤∣S∣≤10^3),表示待操作的初始字符串。

接下来的 N 行,每行给出一条操作指令。根据操作种类,分别为以下格式:

1 s1:对应查找操作,查找字符串 s1在当前字符串 T 中前 3 次出现的位置。
2 p s2:对应插入操作,将字符串 s2插入到当前字符串 T 中“下标为 p 的字符”之前。当 p=∣T∣ 时,表示插入到字符串末尾。
3 l r:对应翻转操作,将当前字符串 T 中下标从 l 到 r 的连续子串翻转。

字符串下标从 10 开始。保证所有输入中的字符串都只包含大小写英文字母和数字,且满足:1≤∣s1∣≤5,1≤∣s2∣≤10。

对于第二类和第三类操作,保证输入下标合法,即第二类操作满足 0≤p≤∣T∣,第三类操作满足 0≤l≤r<∣T∣。

说明:对于任意字符串 X,∣X∣ 表示字符串 X 的长度。

输出格式

对于第一类操作,按从小到大的顺序输出查找到的所有位置(即目标字符串的第一个字符在当前字符串中的下标),相邻两个位置之间用 1 个空格分隔。如果不足 3 次,就按实际查找到的次数输出;如果一次也没有找到,输出 -1。

注意:只要位置不同,就算是不同次出现,出现的字符串允许相互重叠。例如 ababa 中出现了 2 次 aba,位置依次为 0 和 2。

对于第二类和第三类操作,输出操作后的结果字符串。

输入样例

10
ababa
1 a
1 aba
1 aca
2 0 X
2 6 Y
2 3 M
3 2 6
3 4 4
1 aa
3 0 7

输出样例

0 2 4
0 2
-1
Xababa
XababaY
XabMabaY
XaabaMbY
XaabaMbY
1
YbMabaaX

数据约定

题目中设置三个单一操作的数据,对应三种不同的操作,三个数据加起来分配不超过 75% 的分数。

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

大字符串,会用insert,find,reverse就是水题

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    int n;
    cin>>n;
    string s;
    cin>>s;
    while(n--){
        int type;
        cin>>type;
        if(type==1){
            string Find;
            cin>>Find;

            int cnt=0;
            int start=0;
            while(cnt<3){
                size_t pos=s.find(Find,start);
                if(pos==string::npos){
                    break;
                }
                cout<<(cnt?" ":"")<<pos;
                start=pos+1;
                cnt++;
            }
            cout<<endl;
        }else if(type==2){
            int p;
            string s2;
            cin>>p>>s2;
            string Front=s.substr(0,p);
            string Back =s.substr(p);
            s=(Front+s2+Back);
            cout<<s<<endl;

        }else if(type==3){
            int l,r;
            cin>>l>>r;
            reverse(s.begin()+l,s.begin()+r+1);
            cout<<s<<endl;
        }
    }

    return 0;
}


L2-1 姥姥改作业

分数:25

🔖 本题小目录

题目描述

在这里插入图片描述

在没有拼题 A 的很久很久以前,姥姥不得不人工批改学生们交上来的大量作业。有些学生的作业写得实在太乱了,姥姥一眼看到就血压飙升,赶紧放到一边,等冷静下来再说……

简而言之,面对 n 本学生作业,姥姥批改作业的策略是这样的:

1. 为每一本作业定义一个“混乱指数” c_i(i=1,2,⋯,n);
2. 为自己定义一个不可以接受的混乱指数阈值 T;
3. 当看到一本作业的 c_i > T,则先放到一边,即将这个作业本叠放在自己左右手边的作业本堆 S_left 上;
4. 对于 c_i ≤ T 的作业,批改之后叠放在自己左右手边的作业本堆 S_right 上;
5. 当面前没有待批改的作业本时,如果左手边还有一堆作业本,则调整自己的阈值 T 为这堆作业的混乱指数的平均值的向下取整,然后开始批改;
6. 重复上述步骤,直到所有作业都被批改完成。

问:姥姥批改作业的顺序是怎样的?

输入格式

输入第一行给出 2 个不超过 10^3 的正整数:n 为作业本的数量,T 为姥姥可以接受的混乱指数阈值。随后一行给出 n 个不超过 10^3 的非负整数,按原始作业堆自顶向下的顺序,第 i 个数字对应编号为 i 的作业的混乱指数(i=1,2,⋯,n)。同行数字间以一个空格分隔。

输出格式

按照姥姥批改作业的顺序,在一行中输出每个作业本的编号。数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例

10 2
233 8 1 6 0 745 2 555 42 3

输出样例

3 5 7 10 9 4 2 1 8 6

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制256 MB
栈限制8192 KB

思路

先按初始阈值 T 把作业分成两类:大于 T 的放进左堆,小于等于 T 的直接批改并加入答案。等当前这一轮处理完,如果左堆还有作业,就把左堆作为新的待处理序列,阈值更新为左堆混乱指数平均值的向下取整,再继续按同样规则分流。

实现时要注意左堆是“堆栈”式处理,下一轮需要按原顺序逆转后再重新判断,最终按批改顺序输出编号即可。

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    int n,T;
    cin>>n>>T;
    vector<pair<int,int>> CC(n);
    for(int i=0;i<n;i++){
        cin>>CC[i].first;
        CC[i].second=i+1;
    }

    vector<int> res;//结果堆
    vector<pair<int,int>> curHun;//当前混乱堆
    
    for(int i=0;i<n;i++){
        if(CC[i].first>T){
            curHun.push_back(CC[i]);//塞到混乱区
            //初始化当前混乱堆
        }else{
            res.push_back(i+1);//
        }
    }//初次分类

    if(curHun.empty()){
        for(int i=0;i<n;i++){
            if(i)cout<<' ';
            cout<<res[i];
        }
        cout<<endl;
        return 0;
    }

    
    vector<pair<int,int>> nextHun=curHun;//下一次混乱堆

    while(!(nextHun.empty()&&curHun.empty())){
        //只要有一堆还没空
        reverse(nextHun.begin(),nextHun.end());
        curHun=nextHun;
        nextHun={};

        int ave=0;
        for(int i=0;i<curHun.size();i++){
            ave+=curHun[i].first;
        }
        ave/=(curHun.size());
        

        for(int i=0;i<curHun.size();i++){
            if(curHun[i].first>ave){
                //大于平均值的塞一边
                nextHun.push_back(curHun[i]);
            }else{
                res.push_back(curHun[i].second);
                //curHun.erase(curHun.begin()+i);
            }
        }
        if(nextHun.empty()){
            break;
        }

    }


    for(int i=0;i<n;i++){
        if(i)cout<<' ';
        cout<<res[i];
    }
    cout<<endl;

    return 0;
}

L2-2 超参数搜索

分数:25

🔖 本题小目录

题目描述

神经网络模型的超参数是训练前需预先设定的参数,直接影响模型性能。在机器学习过程中需要对超参数进行优化,给学习器选择一组最优超参数,以提高学习的性能和效果。假设我们记录了一系列不同参数组合在验证集上的性能得分(如准确率),本题就请你找出性能得分最高的参数组合。更进一步,对于工程师提出的任一个目标性能得分 x,你也要从所有性能得分大于x 的参数组合中,找到那个得分最小的组合。

输入格式

输入第一行给出正整数 n(1<n≤10^5),为所有在验证集上跑过的参数组合的总量。于是我们将所有参数组合从 1 到 n 进行编号。第二行给出 n 个区间 [0,10^8] 内的整数,第 i 个数字表示编号为 i 的参数组合的性能得分。随后一行给出正整数 m(≤n/2),为工程师查询次数。接下来 m 行,每行给出一个查询的目标性能得分 x,同样在区间 [0,10^8] 内。

输出格式

首先第一行按升序列出所有性能得分最高的参数组合的编号。同行数字间以 1 个空格分隔,行首尾不得有多余空格。
随后对每一次查询 x,我们需要从所有性能得分大于 x 的参数组合中,找到并输出那个得分最小的组合编号。如果这样的参数组合不唯一,则输出编号最小的解。如果这样的参数组合不存在,则输出 0。

输入样例

10
87 91 65 72 95 84 77 95 91 85
3
87
75
95

输出样例

5 8
2
7
0

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制256 MB
栈限制8192 KB

思路

题目理解
有 n 个参数组合,编号 1到n,每个有一个性能得分(整数,0到1e8)。

第一问:输出所有 最高得分 的参数组合编号,按编号升序。

第二问:给出 m 个查询,每个查询给出目标值 x。
在所有 得分大于 x 的组合中,找到 得分最小 的那个组合。
如果这样的组合有多个(得分相同),取 编号最小 的。
如果不存在,输出 0。
只要有二分就实现了
可以把所有组合按 (得分, 编号) 排序,默认排序规则就是先按得分升序,得分相同按编号升序。

排序后,对于查询 x,只需找到第一个 得分 > x 的位置。
由于相同得分已经按编号升序排列,这个位置上的组合就是:

得分是大于 x 的最小得分

且在该得分组里编号最小

二分查找(upper_bound 或手写)即可。如果没找到(所有得分都 ≤ x),输出 0。

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    int n;
    cin>>n;
    vector<pair<ll,ll>> arr(n);

    ll Max=LLONG_MIN;
    ll Min=LLONG_MAX;

    for(int i=0;i<n;i++){
        cin>>arr[i].first;

        Max=max(Max,arr[i].first);

        arr[i].second=i+1;
    }
    vector<pair<ll,ll>> temp=arr;//备用
    sort(arr.begin(),arr.end());

    for(int i=0;i<n;i++){
        if(arr[i].first==Max){
            cout<<arr[i].second;
            if(i!=n-1){
                cout<<" ";
            }
        }
    }
    cout<<endl;

    int m;
    cin>>m;

    while(m--){
        ll x;
        cin>>x;
        bool Found=false;

        int l=0,r=n;
        int ans=0;
        while(l<r){
            int mid=l+(r-l)/2;
            if(arr[mid].first>x){
                Found=true;
                ans=arr[mid].second;
                r=mid;
            }else{
                l=mid+1;
            }
        }

        if(!Found){
            cout<<0<<endl;
        }else{
            cout<<ans<<endl;
        }


    }

    return 0;
}

L2-3 森林藏宝图

分数:25

🔖 本题小目录

题目描述

姥姥手里有一张森林藏宝图(别问怎么得到的),图中标记了森林的入口,还画了通往多个藏宝地的小路。画这张藏宝图的人,还贴心地为每条小路标记了一个“安全系数”,是区间 [0,100] 中的整数。
为了方便规划,姥姥给每个有分叉的路口、以及每个藏宝地都做了编号,其中森林的入口编号为 0。略加研究后,姥姥有了一个重要的发现:如果我们一路向前不走回头路,那么从 0 号入口到每个藏宝地的路径都是存在且唯一的!换言之,我们不可能从两条不同的岔路殊途同归地走到同一个藏宝地。此外,从入口沿任何一条小路一直走到尽头,都会到达一个藏宝地。
姥姥不打算冒太大风险,所以只打算沿着安全系数比较大的路走。本题就请你帮个忙,看看如果只考虑途经最小的安全系数最大的路径,有可能取到哪些宝藏?即从 0 号入口到该藏宝地路径上,所有小路安全系数的最小值最大。

输入格式

输入在第一行给出图中标记的顶点总数 n(1<n≤10^5)
如题面所述,森林的入口编号为 0,其它节点(分叉路口和藏宝地)的编号从 1 到 n−1。随后 n−1 行,第 i(1≤i<n)行给出编号为 i 的节点的前驱节点的编号 j、以及从 j 到 i 这条小路的安全系数 sji​(0≤sji​≤100)。一行中的数字间以空格分隔。
注:所谓节点 i 的“前驱节点”,是指从 0 号入口出发,到达 i 之前所到达的那个节点。因为从 0 号入口到每个藏宝地的路径都是不唯一的,容易证明每个节点的前驱节点也是唯一的。

输出格式

首先在第一行输出解的途经最小安全系数的最大值 —— 所谓“解”,即按题目要求给姥姥推荐的藏宝地,也就是从 0 号入口出发,到该藏宝地路径上所有小路安全系数的最小值最大。
第二行按递增顺序输出解的编号。编号间以一个空格分隔,行首尾不得有多余空格。

输入样例

9
0 30
0 10
0 0
1 8
1 15
2 20
4 40
5 10

输出样例

10
6 8

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制64 MB
栈限制8192 KB

思路

题目重述
有一张森林藏宝图,入口编号为 0。
每个节点(岔路口或藏宝地)都有唯一的前驱节点,所以整个地图实际上是一棵 有根树(根为 0)。
每条边有一个安全系数(0~100)。
从根到每个叶子节点(藏宝地)的路径是唯一的。

定义一条路径的“安全值” = 该路径上所有边权的最小值(最危险的那段决定整体风险)。
现在想找一个藏宝地,使得从根到它的路径安全值 尽可能大。
输出这个最大值,以及所有达到该最大值的藏宝地编号(递增)。

本质理解
这是一个 树上的“最大瓶颈路径”问题。
等价于:在所有根到叶子的路径中,找出 路径上边权最小值 最大的那些叶子。

换句话说:

每条路径的“弱点”是它最小的那条边。

我们想让这个“弱点”尽可能强。

然后输出这些“最强弱点”对应的藏宝地。

解题思路

  1. 建树
    输入给出了每个非根节点的父节点和边权,直接建邻接表(存孩子和边权)。
    根为 0,叶子节点就是没有孩子的节点。

  2. 遍历树,计算每条路径的瓶颈值
    从根开始 DFS 或 BFS,同时维护 从根到当前节点的路径上的最小边权。

初始时,根节点的当前最小边权可以设为一个大数(如 114),因为取 min 时会变成实际边权。

每向下走一条边 w,更新 curMin = min(curMin, w)。

  1. 记录最优解
    遇到叶子节点时:

如果 curMin > bestMin:更新 bestMin,清空结果列表,加入当前叶子。

如果 curMin == bestMin:直接加入结果列表。

如果 curMin < bestMin:忽略。

  1. 输出
    先输出 bestMin,再将结果列表排序后输出。

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

int main(){

    int n;
    cin>>n;
    vector<vector<pair<int,int>>> graph(n); 
    for(int i=1;i<n;i++){
        int j,s;
        cin>>j>>s;
        graph[j].emplace_back(i,s);
    }

    stack<pair<int,int>> stk;
    stk.push({0,114});

    int bestMin=-1;
    vector<int> res;

    while(!stk.empty()){
        auto [node,curMin]=stk.top();//获取当前节点和当前路径上的最小权值
        stk.pop();

        if(graph[node].empty()){
            //如果这个节点的子节点是空的
            //说明是叶子节点
            if(bestMin<curMin){//结果还可以更新
                bestMin=curMin;
                res.clear();//那就可以全清了
                res.push_back(node);

            }else{
                if(curMin==bestMin){
                    res.push_back(node);
                }
            }
        }else{
            //不是叶子节点
            for(auto &[child,ss]:graph[node]){
                int newMin=min(ss,curMin);//在下一个节点的权值和当前最小权值里找一个最小的
                stk.push({child,newMin});
            }
        }
    }

    cout<<bestMin<<endl;

    sort(res.begin(),res.end());
    for(int i=0;i<res.size();i++){
        if(i)cout<<" ";
        cout<<res[i];
    }cout<<endl;

    return 0;
}

L2-4 大语言模型的推理

分数:25

🔖 本题小目录

题目描述

神经网络模型的超参数是训练前需预先设定的参数,直接影响模型性能。在机器学习过程中需要对超参数进行优化,给学习器选择一组最优超参数,以提高学习的性能和效果。假设我们记录了一系列不同参数组合在验证集上的性能得分(如准确率),本题就请你找出性能得分最高的参数组合。更进一步,对于工程师提出的任一个目标性能得分 x,你也要从所有性能得分大于x 的参数组合中,找到那个得分最小的组合。

输入格式

输入第一行给出正整数 n(1<n≤10^5),为所有在验证集上跑过的参数组合的总量。于是我们将所有参数组合从 1 到 n 进行编号。第二行给出 n 个区间 [0,10^8] 内的整数,第 i 个数字表示编号为 i 的参数组合的性能得分。随后一行给出正整数 m(≤n/2),为工程师查询次数。接下来 m 行,每行给出一个查询的目标性能得分 x,同样在区间 [0,10^8] 内。

输出格式

首先第一行按升序列出所有性能得分最高的参数组合的编号。同行数字间以 1 个空格分隔,行首尾不得有多余空格。
随后对每一次查询 x,我们需要从所有性能得分大于 x 的参数组合中,找到并输出那个得分最小的组合编号。如果这样的参数组合不唯一,则输出编号最小的解。如果这样的参数组合不存在,则输出 0。

输入样例

10
87 91 65 72 95 84 77 95 91 85
3
87
75
95

输出样例

5 8
2
7
0

限制

项目限制
代码长度16 KB
时间限制400 ms
内存限制256 MB
栈限制8192 KB

思路

题意简述
给定一个 n 个节点的有向图,每条边有一个权值(代表概率)。从某个起点出发,每次在当前节点的所有未访问过的后继节点中,选择权值最大的那个;若权值相同则选择编号最小的。重复此过程直到无后继或所有后继均已访问。对每个查询输出行走的节点路径。

图的存储:邻接表,每个元素为 (to, weight)。

对每个查询:

标记 visited 数组,记录当前 DFS 路径已访问的节点(注意:每次查询独立重置)。
递归或循环模拟选择过程:
遍历当前节点的所有邻接点,选出满足 !visited[to] 且 weight 最大(次关键码为 to 最小)的节点。
若找到则继续,否则终止。
输出路径。

代码(c++)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;

vector<bool> visited;
vector<int> result;
void DFS(int idea,vector<vector<pair<int,int>>> &graph){
    result.push_back(idea);
    visited[idea]=true;

    int next=INT_MAX;
    int maxP=-1;
    if(graph[idea].empty()){//没有子节点
        return;
    }

    for(auto &[nex,curP]:graph[idea]){
        if(curP>maxP&&!visited[nex]){
            maxP=curP;
            next=nex;
        }
        if(curP==maxP&&nex<next&&!visited[nex]){
            next=nex;
        }
    }
    if(next!=INT_MAX&&maxP!=-1){//防止bug,理论不会产生
        DFS(next,graph);
    }else{
        return;
    }
    
}

int main(){

    int n,m;
    cin>>n>>m;
    vector<vector<pair<int,int>>> graph;//graph[u]表示从u开始的节点,graph[u]由数个{v,w}组成
    //每个{v,w}表示从u到v的权值,也就是概率p
    graph.resize(n+1);//从1-n编号

    while(m--){
        int id1,id2,p;
        cin>>id1>>id2>>p;
        graph[id1].emplace_back(id2,p);
    }

    

    int k;
    cin>>k;
    while(k--){
        int idea;
        cin>>idea;
    
        visited.assign(n+1,false);
        result.clear();
        DFS(idea,graph);

        for(int i=0;i<result.size();i++){
            if(i){
                cout<<"->";
            }
            cout<<result[i];
        }
        cout<<endl;

    }

    return 0;
}

注:L3题目难度较大,依旧第一题能做二三题送命
到时候会附上多一点图片和b站视频链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值