洛谷学习总集1

洛谷代码学习总集 1

目录索引



因为在学习STL,所以有些代码使用 vector / set / unordered_map 实现了,但是除了声明不一样,使用和传统数组类似。

B2002 Hello瓦露多 || P3954 给公式算总成绩

考核你的 输入输出流/基本表达式运算 掌握的能力 不多说
洼路多代码
#include<iostream>
int main(){
    std::cout<<"Hello,World!";
}



总成绩代码
#include<iostream>
using std::cin,std::cout;
int main(){
    int total{0};
    int input;
    cin>>input;
    total+=(0.20*input);
    cin>>input;
    total+=(0.30*input);
    cin>>input;
    total+=(0.50*input);
    cout<<total;
}



P5707 给定路程速度 算出门的24小时制时间

思路:把所有时间换算成分钟单位的绝对时间(例如 8:25 就等于 8×60 + 25 分钟),算上必然的10分钟和路程时间去作差,如果是负数就在前一天,然后再除60和求模得到(小时:分钟)

  • 注意“退位”,如果出发时间在前一天,分钟得到负值需要让小时-1。
  • 注意手动取整
  • 注意补0
点击查看代码
#include<iostream>
#include<cmath>
#define ARRIVEHOUR (8*60)

int main(){
    int s;
    int v;
    std::cin>>s>>v;
    //float t{1.000};
    int Mins{ARRIVEHOUR-10};
    int Hours;
    int t;
    if(s%v==0) t=s/v;
        else t=s/v+1;
    Mins -= t;
    switch(Mins < 0){
        case false: Hours = (Mins/60);        Mins = Mins%60;     break;
        default:    Hours = 23 + (Mins/60);   Mins = 60 + Mins%60;break;
    }
    
    if(Hours < 10)
        std::cout<<0;
    std::cout<<Hours<<':';
    if(Mins  < 10)
        std::cout<<0;
    std::cout<<Mins;

    return 0;
}



P5717 给定三角形三边判断类型

逻辑优化

思路:基本思路很简单,判断输入三边的关系,先判断是不是三角形,再判断锐角钝角直角,再判断等腰以及等边。
坑点:输入的三边并不确定大小顺序,如果只判断一次,可能会因为“巧合”错下结论
解决思路

  • 暴力求解:直接对三边都进行判断。可以直接扩写条件表达式,也可以来回“交换”三边的判断顺序
  • 排序求解:先对三边进行排序,确认长短边关系的同时也可以同时得到是否等腰/等边
点击查看代码

暴力求解(循环判断): 通过对下标求模完成从边0到边2的循环判断

#include<iostream>
using std::cin,std::cout;
int main(){
    int edge[3];
    cin>>edge[0]>>edge[1]>>edge[2];
    int a,b,c;
    int count{0};
    for (int i = 0; i < 3; i++)
    {
        a = edge[i%3]; 
        b = edge[(i+1)%3];
        c = edge[(i+2)%3];
        if(a+b>c){
            count++;
        }
    }
    if (count<3) {cout<<"Not triangle\n";return 0;}
    for (int i = 0; i < 3; i++)
        {
            a = edge[i%3]; 
            b = edge[(i+1)%3];
            c = edge[(i+2)%3];
            a = a*a;b = b*b;c=c*c;
            if(a+b==c){
                cout<<"Right triangle\n";
                count = 0;
                break;
            }
            else if(a+b>c){
                count-=114;
            }
            else if(a+b<c)
                count=114514;
            //cout<<count<<"\n";
        }
    

        if(count > 0){cout<<"Obtuse triangle\n";}
        else if(count < 0){cout<<"Acute triangle\n";}
        if(a==b || b==c || a==c){
            cout<<"Isosceles triangle\n";
            if(a==b && b==c){
                cout<<"Equilateral triangle\n";
            }
        }

    

    return 0;
}

我也比较想写排序写法,等有时间的




P1055 校验码对比和校正

思路:数据处理方式存在差异

  • 把编码以字符串方式读入,然后对各位数码字符转成整数并且求解,遇到'-'直接跳过
  • 把编码方式以数字方式读入,然后求模得到各位数字,利用scanf格式控制可以避开输入‘-’(输入流需要:var <<'-'<< var )

操作区别
两种不同数据处理方式在输出上有些不同

  • 如果直接以std::string(c的话char* 同理)读入,那么我们算完校验码可以直接把最后一位重新赋值后再把那个std::string对象送出去
  • 如果数字方式读入,构建字符串再输出有些过于麻烦,一个一个数字或者几个数字循环送到输出流同时特定位置插入‘-’效果会好一些。
    坑点:没啥坑点,注意校验位如果是10,需要换成‘X’表示
点击查看代码 我是第一种思路 (inline显式定义了内联函数,编译器会像宏定义一样在编译时直接在对应位置展开函数体内容,而不是反复跳转)(纯编程习惯 没有什么影响)
#include<iostream>
using std::cin,std::cout;
inline int convert(char c){
    if(c == 'X')
        return 10;
    return (c-'0');
}
inline int convert(int i){
    if(i == 10)
        return 'X';
    else
        return (i+'0');
}

int main(){
    std::string input;
    cin>>input;
    int sum{0};
    int count{1};
    for (int i = 0; i < 11; i++)
    {
        if(i==1 || i==5){
            continue;
        }
        sum+=(convert(input[i])*count);
        //cout<<convert(input[i])<<"*"<<count<<'\n';
        count++;
        
    }
    count = sum % 11;
    //cout<<count;

    if(input[12]!=convert(count)){
        input[12] = convert(count);
        cout<<input;
    }
    else 
    cout<<"Right";
}



P2615 生成奇数阶幻方

思路:(如果不知道幻方生成算法)如果把给定示例放到execl表格中手动构建一下,发现其实不过是在做一件事:尝试把数字放在当前位置右上角的格子处,但是:
- 如果格子被占用,放在当前格子下方
- 如果右上角超出边界,则从另一侧边界进入放下数字。(有点像v社的传送门游戏,上下两条边界和左右两条边界对应建立两对传送门,数字从右边界出去会落在左边界右侧,从上边界出去会落在下边界上侧)
- 初始数字为1, 放在首行中间
坑点
- 题目描述墨墨唧唧,故意要把人绕进去
- 如何实现 “传送门机制”
- 动态建立方阵 / 把所需空间建立在静态的39阶方阵中
- 判断位置是否被占用(有些编译器在开拓栈空间的时候会清零或者初始化为固定的数,此处我们假定初始化时数组内为随机的数)
(c语言中 c99以上支持通过 array[N]{0}或者string[N]{"\0"}或者something[N]{}来初始化为零值,cpp11同理)
但是只能初始化为零值

点击查看代码

我本来想将把int写个别名叫position
然后寻思着通过position类型 运算符重载求模运算实现传送门机制(求模可以实现 单向传送门,例如假定n为坐标,大小为3:n=3时,对3求模就变成0)
(什么异想天开 一拍脑袋 脑洞大开)
但是不可以对int类型的运算符进行运算符重载啦~

注意:
如果想要通过把n赋给const int实现控制“数组大小”,这是非常“不推荐的做法”(而且仅支持c99以上,因为c99之前要求变量声明必须统一在函数开头),你会发现开启O2优化后程序执行结果会出现问题。

#include<iostream>
using std::cin,std::cout;
inline int abs(int i){
    return ((i>0)*i+(i<0)*(-i));
}
using position = int;

inline position limitToSize(int i,int size){
    if(i>0)
        return i%size;
    else if(i<0)    
        return (i%size)+size;
    else 
        return 0;
}


int main(){
    int n;
    cin>>n;
    const int size{n};
    int magic_square[39][39];
    bool tag[39][39]{false}; //我使用布尔数组,可以稳定初始化为false值,也可以通过变成位来优化大小
    position i = 0;
    position j = size/2; //坐标,但是其实如果用指针会更像c hhhhhhh
    for (int num = 1; num <= size*size; num++)
    {
        
        if (!tag[i][j])
        {
            magic_square[i][j] = num;
            tag[i][j]=true;
            i = limitToSize(i-1,size);
            j = limitToSize(j+1,size);  //移动到右上角
        }
        else{
            j = limitToSize(j-1,size);
            i = limitToSize(i+2,size); //退回原位置的同时下移一格
            //我们的函数会把给定值双向的限制在size范围内,
            //所以对一个数进行对称的操作相当于无操作
            magic_square[i][j] = num;
            tag[i][j] = true;
            i = limitToSize(i-1,size);
            j = limitToSize(j+1,size);
        }
        
    }

    for (int a = 0; a < size; a++)
    {
        for (int b = 0; b < size; b++)
        {
            cout<<magic_square[a][b]<<' ';
        }
        cout<<std::endl;}
    return 0;
    }





P1914 凯撒密码实现

思路:肥肠滴简单阿,我们的字母ascii码与字母本身在字母表的位置存在着唯一映射关系(人话:ascii码稳定等于字母序号+某个定值(也就是’a‘的ascii值))
直接对字符串里面每一个元素都还原成字母表序号,对26求模,再还原为字符就好了 oriPasswd[i]=((oriPasswd[i]-'a'+unit)%26)+'a';

坑点题目描述无用信息太多
- 可能对ascii码和字符的理解要求比较高?
- 没了,题本身没什么陷阱或者高难度

点击查看代码
#include<iostream>
using std::cin,std::cout;
int main(){
    int unit{0};
    std::string oriPasswd;
    cin>>unit;
    cin>>oriPasswd;
    for (int i = 0; i < oriPasswd.length(); i++)
    {
        oriPasswd[i]=((oriPasswd[i]-'a'+unit)%26)+'a';       
    }
    cout<<oriPasswd;
    
}



P1308 find + word count

思路:先匹配首字母,再查找前后是否有空格(同时可以判断单词长度是否一致)(这意味着他是单独的词),然后再挨个字母去判断是否为同一个词
(例如: abc和bbc显然不是同一个词,abc和abcc显然不是同一个词,abc和acc显然不是同一个词,先首先判断容易判断的要素)
坑点
- 不区分大小写,意味着每一个字母都要同时确认两个字符
- 首单词和尾单词分别在前和后都没有空格,要针对特例单独分析,所以在这些位置就可以不检测空格了(取这些位置会越界 属于未定义行为!仅仅比较问题不大,但是切记不要去赋值!
- scanf()和输入流会将空格视作分隔符,输入句子请使用getline(),或者循环输入单词直到读取换行符
- 计数器。必须将所有字符都成功匹配才能进行加一。

点击查看代码
#include<iostream>
using std::cin,std::cout;

inline char con(char c){
    if(c>='A'&&c<='Z')
        return c+('a'-'A');
    else if (c>='a'&&c<='z')
        return c-('a'-'A');
    else return '\0';
}


int main(){
    std::string word;
    std::string sentence;
    int pos{-1};
    int count{0};
    cin>>word;
    cin.ignore();

    getline(cin,sentence);
    for(int i = 0; i <= sentence.length();i++){
        bool terminate{false};
        bool has_theSame_First_Char{(word[0]==sentence[i] || con(word[0])==sentence[i])};
        bool is_aSingle_Word{(i==0||sentence[i-1]==' ')&&(i+word.length()==sentence.length()||sentence[i+word.length()]==' ')};
        
        if(has_theSame_First_Char&&is_aSingle_Word){
            for (int j = word.length()-1; j > 0; j--)
            {
                if(word[j]!=sentence[i+j]&&con(word[j])!=sentence[i+j]){
                    terminate = true; //如果匹配失败,中途中断,读取这个标志,计数器不会加一
                    break;
                }
            }
            if (!terminate)
            {
                count++;
                
                if(pos==-1) pos = i;
                i+=word.length(); //由于要求必须检测到的是完整单词,所以在检测成功后我们可以直接跳到下个单词
                //有更高效的办法,外层循环直接以单词为索引,而不是以字母为索引,这需要检测分隔符数量,或者循环读入并且分别存在数组元素中。
                //我这里相当于用传统的子串查找办法,而且是暴力版本。很低效
            }
        }
        
    }

    if(pos!=-1)
        cout<<count<<' '<<pos<<'\n';
    else
        cout<<pos<<'\n';
    
    return 0;

}
//To be or not to be is a question -- 沙比



P5740 最值判断

思路:每个学生建立一个结构体,使用结构体数组,方便求完最值后回溯到对应元素
坑点:没啥坑点,最值判断的时候记录好最值对应的下标,输入的时候格式化做好,就行。

点击查看代码
    #include<iostream>
    #include<vector>
    using std::cin,std::cout;

    typedef struct{
        std::string name;
        int zhcn;
        int math;
        int eng ;
    }studentInfo;

    int main(){
        int max[2]{0,0};
        int n;
        cin>>n;
        std::vector<studentInfo> input;
        for (int i = 0; i < n; i++){
            studentInfo t;
            cin>>t.name;
            cin>>t.zhcn;
            cin>>t.math;
            cin>>t.eng ; //或者也可以直接cin>>t.name>>t.zhcn>>t.math>>t.eng;
            int sum = t.zhcn + t.math + t.eng;
            if(max[0]<sum){
                max[0] = sum;
                max[1] = i  ;
            }

            input.push_back(t);
        }
        cout<<input[max[1]].name<<' '<<input[max[1]].zhcn<<' '<<input[max[1]].math<<' '<<input[max[1]].eng<<'\n';
        

    }



posted @ 2024-09-28 16:59  Brakeintime  阅读(61)  评论(0)    收藏  举报