SMU-ACM集训队2024 3rd

文章讲述了作者在SMU2024春季天梯赛中的算法学习与实践,涉及二进制思维、STACK函数、reverse函数、substr函数、BFS和Floyd算法,同时强调了补题过程中的经验和教训,如栈操作错误检查和避免除数为零的浮点错误。
摘要由CSDN通过智能技术生成


The Third Week

耐心和恒心总会得到报酬的。 ————爱因斯坦

一、前言

同上啊周二天梯,有一道题卡了很久但其实不太有价值,还导致我后面的题目做崩了,所以ioi赛制还是很需要注意时间的。
周六天梯训练赛,好几题差一俩分没拿满,在举特例方面能力非常薄弱,很多情况都会考虑不到,最后还有一点时间的时候不太想写了,以后也要注意调整状态,坚持到最后一刻。
周六晚上atcoder,只做出了ab,c题给我做的WA,RE,TLE就是没AC。
周日补题补题补题。


二、算法

1.二进制

<1>(SMU 2024 spring 天梯赛2 7-8)

前世档案 20分
写出来了但是暴力费时
题解:
输入n为问题数量,m为玩家人数即询问次数,输出每行答案代表得到哪个结论
二进制思维,敏感度
代码:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n,m;
    cin>>n>>m;
    while(m--){
        string s;
        cin>>s;
        int ans=1;                          //结论从1开始标号
        for(int i=0;i<s.length();i++){
            if(s[i]=='n'){                  //代表这个位置的1
                ans+= 1<<(n-i-1);           //左移多少位后
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
#include<iostream>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n,m;
    cin>>n>>m;
    int nn=1;
    for(int i=0;i<n;i++){
        nn=nn*2;
    }
    while(m--){
        char bh[n];
        int l=1,r=nn;
        int mid=(l+r)/2;
        for(int i=0;i<n;i++){
            cin>>bh[i];
            if(bh[i]=='y'){
                r=mid;
            }
            else if(bh[i]=='n'){
                l=mid;
            }
            mid=(l+r)/2;
        }
        cout<<r<<endl;
    }
}

原暴力ac代码,题目较简单,重在思维算法。

2.STACK函数

<1>(SMU 2024 spring 天梯赛3 7-10)

列车厢调度 25分
20分的代码,最长序列的一般可行情况没过,改不了。
题解:
三节车厢,给出两行字符串仅包含26个大写字母且不重复,第一行代表原车厢,第二行代表需要的进栈顺序,用stack进出即可。
代码:

#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;

stack<char>s1,s2,s3;
int a[100];
int b[100];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    string c1,c2;
    cin>>c1>>c2;
    int t1=0,t2=0;
    int m=0;
    for(int i=0;i<=60;i++){{
            if(s2.size()==c2.length()){
                m=i;
                break;
            }
        }
        if(c1[t1]==c2[t2]){
            s2.push(c1[i]);
            a[i]=1;
            b[i]=2;
            t2++;
            t1++;
        }
        else {
            if(s3.empty()){
                s3.push(c1[i]);
                a[i]=1;
                b[i]=3;
                t1++;
            }
            else if(s3.top()==c2[t2]){
                s2.push(s3.top());
                s3.pop();
                a[i]=3;
                b[i]=2;
                t2++;
            }
            else {
                s3.push(c1[i]);
                a[i]=1;
                b[i]=3;
                t1++;
            }
        }
    }
    if(s1.size()!=0||s3.size()!=0){
        cout<<"Are you kidding me?"<<endl;
    }
    else {
        for(int i=0;i<m;i++){
            cout<<a[i]<<"->"<<b[i]<<endl;
        }
    }
    return 0;
}

3. reverse函数

引入头文件algorithm
可用于反转int和char数组,用法类似于reverse(a,a+10)
当反转字符串时,需要begin()和end()左右翻转,用法见下方代码。

<1>(SMU 2024 spring 天梯赛2 7—6)

福到了 分数十五分
解释一下当时是做出来的,但实在太暴力了我自己也受不了,就去学了一下这个函数。
题解:
题意较为简单,输入字符fh和行数n,以及一份网格,输出网格翻转后的图形,并将每个@替换为fh,如果网格翻转后与原来相同则多输出一行bu yong dao le。
一个vector数组ms储存字符串,swap上下颠倒,reverse左右颠倒,与原来相同就输出一句话和网格,否则直接输出网格。
代码:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    char fh;
    int hs;
    cin>>fh>>hs;
    string s;
    getline(cin,s);
    vector <string> ms (hs);
    for(auto &s:ms){
        getline(cin,s);
    }
    auto xs=ms;
    for(int i=0;i*2<hs;i++){
        swap(ms[i],ms[hs-i-1]);
    }
    for(int i=0;i<hs;i++){
        reverse(ms[i].begin(),ms[i].end());
    }
    size_t t=ms[0].length();
    if(ms==xs){
        cout<<"bu yong dao le"<<endl;
    }
        for(int i=0;i<hs;i++){
            for(int j=0;j<t;j++){
                if(ms[i][j]==' ')cout<<' ';
                else cout<<fh;
            }
            cout<<endl;
        }
    return 0;
}
#include<iostream>
#include<vector>
using namespace std;

int zf[102][102]={0};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    char a;
    int n;
    cin>>a>>n;
    int t=0;
    bool p=true;
    char ls;
    string s;
    getline(cin,s);
    for(int i=1;i<=n;i++){
        getline(cin,s);
        t=s.length();
        for(int j=0;j<s.length();j++){
            if(s[j]=='@'){
                zf[i][j+1]=1;

            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=t;j++){
            if(zf[i][j]!=zf[n-i+1][t-j+1]){
                p=false;
                break;
            }
        }
    }
    if(p) {
        cout<<"bu yong dao le"<<endl;
    }
    for(int i=n;i>=1;i--){
        for(int j=t;j>=1;j--){
            if(zf[i][j]==1)cout<<a;
            else cout<<' ';
        }
        cout<<endl;
    }
    return 0;
}

以上是原暴力ac代码,会浪费比较多时间去写。

4. substr函数

字符截取函数
记住就行,比较常用

string h=s.substr(5);    //h为从s的下标为5取到最后
string h=s.substr(5,3);  //h为从s的下标为5取三位

随便举个实用例子吧

<1>(Atcoder ABC347 B)

#include <iostream>
#include <set>
using namespace std;

set<string>a;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    string s;
    cin>>s;
    for(int i=0;i<s.length();i++){
        for(int j=i;j<s.length();j++){
            string t=s.substr(i,j-i+1);
            a.insert(t);
        }
    }
    cout<<a.size()<<endl;
    return 0;
}

5.BFS算法

<1>(SMU 2024 spring 天梯赛3 7-10)

题解:
输入城市个数n,道路条数m,m行输入道路信息,后k行被攻占编号,用俩个bfs构造联通域之后便利输出。
代码:

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

vector <int> sa[5005];
int vis[5005];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        sa[a].push_back(b);
        sa[b].push_back(a);
    }
    for(int i=0;i<n;i++){
        vis[i]=-1;
    }
    for(int i=0;i<n;i++){
        if(vis[i]==-1){
            queue<int>q;
            q.push(i);
            vis[i]=i;
            while(q.size()){                                //一个BFS深搜
                int now=q.front();
                q.pop();
                for(auto& net :sa[now]){              //遍历now的所有邻居
                    if(vis[net]==-1){                       //未访问过的加入队列
                        vis[net]=i;
                        q.push(net);
                    }
                }
            }
        }
    }
    int k;
    cin>>k;
    int cnt=1;
    queue<int>la;


    for(int i=0;i<k;i++){
        int a;
        cin >> a;
        int col = vis[a] + n + 1;
        cnt = 0;
        for (int j = 0; j < n; j++) {
            cout<<vis[j]<<' '<<vis[a]<<endl;
            if (vis[j] == vis[a] && j != a) {
                vis[j] = col;
                cout<<j<<endl;
            }
        }
        vis[a] = -1;                                      //表示已经被攻占
        for(int j=0;j<n;j++){
            if(vis[j]==col){                              //都是一个组的
                la.push(j);
                vis[j]=j;
                while(la.size()){                         //又一个BFS深搜
                    int now=la.front();
                    la.pop();
                    for(auto& net :sa[now]){
                        if(vis[net]==-1){
                            vis[net]=j;
                            la.push(net);
                        }
                    }
                }
                cnt++;                                    //表明组内被攻占城市数
            }
        }

        if(cnt>1){
            cout<<"Red Alert: City "<<a<<" is lost!"<<endl;
        }
        else cout<<"City "<<a<<" is lost."<<endl;
        vis[a]=-1;                                       //复原
    }
    if(k==n){
        cout<<"Game Over."<<endl;
    }
    return 0;
}

6.Floyd算法

<1>(SMU 2024 spring 天梯赛3 7-9)

哈利波特的考试 分数25 分
投机取巧了,复杂度n的三次方但是数据比较小,另外djst算法实在是写不出来。
题解:
输入动物总数n和变形魔咒术m,求由选中的动物变成其它动物的最长路径最小值,并输出自己的编号。
多源最短路径问题,做的挺暴力的看代码吧。
代码:

#include<iostream>
#include<algorithm>
using namespace std;

int g[105][105];
int n,m;
void floyd(){
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                g[i][j]=(g[i][j])>(g[i][k]+g[k][j])?(g[i][k]+g[k][j]):(g[i][j]);
}          //最短路径思维
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int p=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++){
            if(i==j) g[i][j]=0;
            else g[i][j]=1000008;
        }
    }                
    int miin=999999;
    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=c;
        g[b][a]=c;
    }
    floyd();
    for(int i=1;i<=n;i++){
        int max=-1;
        for(int j=1;j<=n;j++){
            if(i!=j&&g[i][j]>max)max=g[i][j];
        }
        if(max<miin){
            miin=max;
            p=i;
        }
    }
    if(!p)cout<<0<<endl;
    else
    cout<<p<<" "<<miin<<endl;
    return 0;
}


三、总结

有关补题的,补题最好能补到至少俩题25分题.
在条件语句 if(s2.top()==c2[t2]) 中,没有检查栈 s2 是否为空就直接调用 s2.top(),如果 s2 为空,则会导致段错误。记住这段文字,价值二十分。
除数或mod数为0会导致浮点错误,有些编译器无法提示错误。
补题的时候发现现在做的题目类型非常多样,要学会融会贯通,举一反三,也要更好地掌握各个算法,不少算法了解了但写起来并不熟练,注重补题吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值