跳转至

Debug Secret Script

提示:

在笔记内部使用了部分C++的内容,和语法

在此声明:

cout<<" " 可以看成C语言中的printf

cin>>" "可以看成C语言中的scanf

string部分采用的是C++,C语言字符串部分由于不常用,所以在内容中尚未体现

引言:

Debug也是一种能力的体现,在算法学习的过程中,不光要学习各种算法知识(c++,stl,数据结构等),还要学会调试代码。 每一个人遇到的bug是不相同的,但我的目的是希望能给初学者在debug的过程中提供一些方向,我所写下的内容不多,希望能给你们带来一些帮助。

前提:

要稳住心态。

谨慎思考,减少罚时。

切忌自大,要仔细检查代码。

灵活使用IDE。

明白题意。

团队赛,要和队友沟通,拓展思路。

比赛意见:

1.标识符命名需规范(简洁的前提是,你能记住每个标识符所代表的含义)

2.电脑死机时,或主动关机的情况下,不要随意乱动,请及时联系工作人员。

3.如果在正式开始的时候,发现无法进入系统,或者系统未安装,请及时联系工作人员。

4.发放账户密码的纸张,请自己盖好,防止其他队伍登录,帮你增加罚时

5.注意观察榜单,挑简单的先做。

6.注意!!!不要在比赛时间尚未开始时翻看试卷。

7.不要有心理压力,放平心态。

8.减少犯罪次数(在提交显示错误之后,请认真修改)

9.不要按Ctrl+Alt+F10,这会使电脑死机。

10.注意保存自己的代码。

11.出现问题,不要求助身边选手,人心叵测。

12.请选择靠谱的队友,能坚持下去和你比赛的。

13.如果遇到提交结果为undefined(测题机坏了),请暂停提交,如果继续提交,很有可能一堆wa下去。

14.禁止作弊!!!

image-20241103191540313

个人感悟:

放平心态,这也是我在经历数次的折磨之后,明白的一件事情,专心调试就好,其他人做完不做完又有什么可在意的呢?(虽然有位学长总是在强调“犯罪”的问题,但这只是为了让你重视和珍惜每一次提交的机会,不要有太多压力,就算爆零又如何?)尽力就好,享受过程,debug是痛苦的,但是在这漫长而又枯燥的过程中,我们理应学会苦中作乐。

一.基础bug

基础bug---IDE能直接识别出的bug。

识别基础bug的最好方式是---打开 IDE

解决方式:阅读IDE反馈的信息

常见的基础bug:

{}问题:很多时候,由于自己的复制剪贴,可能会使得 } 漏掉,这个时候点击测验的时候,可能会显示error}, 因而在写代码的过程中,要注意是否忘记 } 符号。

scanf("%d",&a); //常见的错误就是忘记输入了&(取地址符)

scanf("% ",&);  //%后面的格式转换码与数据类型没有对应   易错点:double 对应%lf

自定义函数时,忘记return;

标识符先前已经定义过:

int a;
int b;
int b=1024;(报错) //不能连续定义同一个变量的数据类型,如果要进行修改可以选择(float)b;或者直接删除此语句
//进行强制修改其类型。

同一变量名称反复定义的方法:

for(int a=0;a<5;a++){

}
for(int a=0;a<5;a++){

}
//在for()内部进行定义

由于变量a是在for循环内部定义的,因此可以重复定义。(有一定风险就是,你得确保这个变量不需要保存数据什么的,因为在外界使用的话,会显示该变量不存在)

!!!.函数传数组:

正确传递数组的方式为:

二.反馈:

在debug的过程中,反馈尤为重要

反馈分为两种: 第一种为IDE所给你的反馈 第二种为自行反馈

自行反馈意味着,你需要自己寻找到错误在哪 常见的做法是,在每一个模块的内部,放置一条测试语句

例如:printf("123");

看看该语句是否在判断语句的内部中实现。(自行找到反馈,计算机不能告诉到底是哪个环节输入错误,但是你可以自己验证并得到反馈) 例如:

if(条件){
   printf("123");
}
检查数组正确输入:
cout<<a[1];
大部分IDE和做题系统,反馈都是英文的,不要求把单词都认的出来,找到关键信息(真正的错误提示)即可;

三.检测时的几种错误:

检测时,要注意输入的是案例几,不要对着案例一进行检测,然后对着案例二的答案进行修改

1.编译错误:

最容易修改的错误,一般根据反馈,找出关键信息,即刻修改。

例如此案例,能清楚的看见计算机将错误的语句反馈给我们,

当然,如果看不懂计算机告诉你的信息,建议一点点的开始看,从找关键符号,和使用翻译软件等方式解决。

2.运行超时:

基本上就是for等循环出现了问题,一般要关注的点有,循环条件,循环变量是否在函数内部进行改变,以至于陷入死循环。

for循环内部无范围限制

例如:for(int i=1;i<5;)    //缺少了i++(变量未改变)

ps : while循环比较特殊,它可以存入特殊循环条件

又或者是for循环中出现

for(int i=0;i<n;j++) //循环变量名称错误!!!

若是使用while循环

请检查:
while(是否是死循环?)
while(是否判断结束循环的条件中,变量的范围是否没考虑)

3.无法在规定时间内运行:

由于时间复杂度过高,导致超时。

解决方法:

通过if等其他手段,减少循环的使用。

可以去学习一些优化操作,如剪枝等;

选择更换做题思路,可能是自己将简单问题复杂化了,算法题的思路有很多种,如果可以,请不要将自己的思维固化。

ps:有些题是无法单纯通过暴力枚举解决的,一味的暴力枚举,换来的代价就是极高的时间复杂度。

在算法的学习中,需要去学会计算时间复杂度

pps:使用#define endl '\n';

endl运行消耗的时间大于\n,为了书写习惯,此定义将完美避开这个问题

4.段错误:

段错误:程序发生段错误,可能是数组越界,堆栈溢出(比如,递归调用层数太多)等情况引起。

一般情况,需要检查的是数组是否开的过多,导致数组越界(数组越界,是因为连续开两个数组,在内存中的排布其实是相连的)

解决方式:如果自己定义的数组过多,可以考虑删除一个数组。

检查自己的数组容量是否开的过大(一般范围是在1e8左右)

下标是否正确?一般是在循环中查看是否循环内容出了问题

函数递归调用过多,检查函数是否内部语句是否正确

ps:检查到底是哪个环节出现问题,通过将部分语句变成注释//,看看是否仍然发生段错误,如果到了最开始时,仍出现段错误,那么只有一种结果就是数组开的过于庞大。

!!!在一些题中,不要根据题目中出现数组,就一味的去使用数组,当出现数组越界的时候,最好是转换思路,同时减少或者避免数组的使用

!!!在做题中,请减少二维数组的使用

5.输入格式不符:

审题不够严谨造成的原因,去检查最后输出格式是否遗漏" "(空格)

或者输出内容是否包含 , '等符号

注意保留几位小数点

如果是四舍五入的问题,注意使用round函数(四舍五入到整数)。使用方式:通过*保留位数的10次幂,再将其除掉。

6.正确率为**%:

如果正确率在50%附近徘徊,说明此题可能分奇数和偶数,随后将输入示例,看看是否和奇数和偶数有关系。

如果正确率较低,那么基本上,切换示例就能够看出来问题出现在哪。(可能是数据范围小了)

多切换案例,想一些极端的例子

又或者由于超时,导致过大的案例未通过(观察运行时间)

四.输入格式与预期不符合:

如此题所示,在使用C++输入时候,如果将一串数据分单个数字存入其中,cin无法实现,请选择使用C语言中的scanf("%1d",a);

%1d意味着,每次只存入一个数。

所以在做题的过程中,需要注意示例的输入格式。

ps:虽然C++在输入输出等层面变得更加便利和迅速,但是C语言的某些功能,仍然是必不可少的,因此,在拓展C++语言的时候,也不要忘记巩固C语言的使用。

!!!同时,观察运行时间是否超时,这种时候一般为超时,以至于有些案例无法正常通过,需要改变解题思路。

五.输入结果为空:

对于debug过程中,最怕的就是没有收获任何的反馈,因此在面临输入结果为空的时候,优先去查看自己的输出语句。

例如:for(  ; ; ){
            cout<<"123";//去检查for循环是否出现问题。
            }
    for( ; ; ){
        c++;
    }
    cout<<c<<endl;  //如果C的输出为空,寻找C变量上一次出现的位置是在什么位置,随后检查相应的语句。

除此之外,还有各种判断语句,都可能影响最后输出为空的情况。

如果是for循环和if的嵌套情况之下,可以通过

cout<<"语句"<<endl;

判断是哪个环节出现错误。

同时,在定义变量的情况之下,有的时候需要将变量进行初始化=0

例如这道题:去除前导0,不只是出现0408这种情况,还有可能会出现00408,因此在debug的过程中,通过判断数组的第一位是否为0是远远不够的,而如果只判断数组是否为0,然后为0不输出0,也是不可取的,因为这样的话,只会留下48。(只判断是否为0,并不是删除前导0,是删除数组中所有的0)

for(int p=0;p<c;p++){
    if(b[p]>0){
        for(;p<c;p++)
       cout<<b[p];//位置正确
    //for(int i=p;i<c;i++)
    //cout<<b[i];   
        break;
    }
}
return 0;
}

错误代码如下:

for(int p=0;p<c;p++){
    if(b[p]>0){
    for(int i=p;i<c;i++)
    cout<<b[i];   
    }
    break;
}
return 0;
}
错误原因:意外使用break;同时break是在for循环中放置,并非是放置在if中,因此造成了输出结果为空。

不难发现,有些时候,都是一些很简单的bug阻止结果的的正确 不过不要因此变得消沉,请相信自己,下一次,绝对不会犯相同的错误。正视自己的失败,才能慢慢变得更加强大

六.书写代码的一些小建议:

1.定义变量:

在定义整型变量的时候,尽量使用long long 而非int,这样可以不用判断数据是否超过Int型。

同理,在定义浮点型的时候,选择使用double,精确度更高,确保小数点的后几位都是正确的。

如果觉得long long在输入过于繁琐,可以选择在开头

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(void)
{
    ll a;//直接输入ll.就代表着long long
}

同时,定义变量名称的时候,尽量避免使用o作为变量名称(因为o可能会和零混用

)

2.观感:

尽量让自己代码的长度缩减一点

使用三项表达式:

表达式1?表达式2:表达式3

格式改变:

例如:
    for(int i=0;i<n;i++){       //将'{'移动到和for同一行的位置来
        cout<<"110";
    }
如果是另一种形式:
    for(int i=0;i<n;i++)
    {
        cout<<"110";
    }

可以看出,优化后的代码减少了一行,这样看可能不太直观,那如果很多个for循环叠加的话?

    for(int i=0;i<n;i++){
     for(int p=0;p<m;p++){
     cout<<"110";
     }
    }
如若不然:
    for(int i=0;i<n;i++)
    {
     for(int p=0;p<m;p++)
     {
     cout<<"110";
     }
    }

仅仅只是两个for循环的嵌套,就能看的出来,将 ' { ' 推进一行,所带来的观感就变得大不相同。

当然,可以稍微紧凑一点,因为过长的代码容易分辨不清各个语句的分布,但是请不要将代码堆砌成密不透风的城墙,同样也会影响读代码时候的速度。

3.输入:

输入数组时,要注意变量名称和变量位置是否正确:

例如: y[i]-x[i],不要写成y[i]-y[i]

做题时,如果要分离每一位数,可以选择定义一个字符串,将每一位数都存入,能大大减小做题难度。

七.删改代码:

删改代码的时候,不要进行遗漏,或者误删,在进行删除之后要重新进行检查。

必要的时候通过使用注释来表明点。

八.多组数据:

在有些题目的样例中,并没有举出多组输入,这往往会被人所忽略,在做题前请认真读题。

int t;//几组数据
while(t--){

}//最外层使用while可以有效的将单组数据的内部循环分清

1.如果要识别换行符:输入

cin.get()='\n'

2.循环输入到结束:

whlie(cin>>n)

九.字符串问题:

在输入数字字符串的时候;

虽然输入的是数字,但是存入的确实字符,因此,如果要将数字输出,需要减去字符‘0’

例如:b[i]-'0';

在字符串题中,我一般使用C++中的string来写

string函数定义:string a;

输入(包括空格的输入)getline(cin,a);

知晓字符串的尺寸 :a.size()

开循环单个输出字符串: for(int i=0;i<a.size();i++) { cout<<a[i]; } 字符串长度length() *非必要,别tm开那么多的数组,无论是字符串数组,还是正常数组 *数组越界,纯自找的,少给自己整点麻烦事!!!!!!!

c++中的size()和length()没有区别 都能算出字符长度

string str:生成空字符串

string s(str):生成字符串为str的复制品

string s(str, strbegin,strlen):将字符串str中从下标strbegin开始、长度为strlen的部分作为字符串初值

string s(cstr, char_len):以C_string类型cstr的前char_len个字符串作为字符串s的初值

string s(num ,c):生成num个c字符的字符串

string s(str, stridx):将字符串str中从下标stridx开始到字符串结束的位置作为字符串初值

eg:

string str1;               //生成空字符串
string str2("123456789");  //生成"1234456789"的复制品
string str3("12345", 0, 3);//结果为"123"
string str4("012345", 5);  //结果为"01234"
string str5(5, '1');       //结果为"11111"
string str6(str2, 2);      //结果为"3456789"
  1. size()和length():返回string对象的字符个数,他们执行效果相同。
  2. max_size():返回string对象最多包含的字符数,超出会抛出length_error异常
  3. capacity():重新分配内存之前,string对象能包含的最大字符数

*string的交换: *void swap(string &s2); //交换当前字符串与s2的值

string的插入:push_back() 和 insert() void test4() { string s1;

// 尾插一个字符
s1.push_back('a');
s1.push_back('b');
s1.push_back('c');
cout<<"s1:"<<s1<<endl; // s1:abc

// insert(pos,char):在制定的位置pos前插入字符char
s1.insert(s1.begin(),'1');
cout<<"s1:"<<s1<<endl; // s1:1abc

}

C++删除string最后一个字符的几种方法

include

include

using namespace std; int main() { string str; str = "123456"; cout << str << endl;

//方法一:使用substr()
str = str.substr(0, str.length() - 1);
cout << str << endl;

//方法二:使用erase()
str.erase(str.end() - 1);
cout << str << endl;

//方法三:使用pop_back()
str.pop_back();
cout << str << endl;
return 0;

}

十.注意使用函数之后,变量转换的类型:

pow( )

sqrt( )

需要在函数前面进行强制性转换(long long)

同时pow( )中,无法使用⅓(不是整除的数)

同时由于整型的存储是有范围的

在计算时,请保持先除(/)后乘(*)的习惯

结尾:

不难发现,有一些部分的案例很少,由于我自身并不喜欢算法,因而有过一个月的时间不去添加笔记上面的内容了。5000字已经是我现在能写的极限了,再加上算法题未来会很少做,所以我打算就此完结。后续的添加、修改和补充,就留给后面的学弟学妹们吧!

写这个笔记的想法,来自于软件2201的杜金雨,特此也感谢他的C语言速通笔记,在我初学C语言的时候,给了我很大的帮助。

虽然笔记名称叫做Debug Secret Script但是,我在里面写的不止是这些,可能有点脱离主题,但是为避免再开一个笔记单独讲这些,还是选择将这一切归纳到一起。从我个人角度来看,这一切是值得的,总归是留下了点什么东西。

面对排名的不如意,我曾多次陷入迷茫,感到失落。在debug过程中,也因为4、5个小时,甚至一天,让自己崩溃。为了防止有人再次陷入相同的问题,再次面临崩溃,希望这个笔记能帮助到你。

如果遇到一名叫葛日波的老师教你C语言,不管你那时C语言是否学完,请认真听讲,会使你受益匪浅。

对UIT的每日签题,我有以下几点要求要讲:

1.切忌上网抄袭,类似题解等东西,其实也不建议看(除非超过四个小时以上未能解开)

2.切忌注重签题排名,每日签题的目的是为了锻炼你的能力

3.不要抄身边人的代码,会有人负责查重

4.不准使用AI辅助做题,在学习阶段,重要的是锻炼你的编码能力

5.IOI赛制,禁止单次提交超过30次

                                               不爱算法、不弱算法

                                                                                        ------数技2401沐沐