一些常用的技巧分享

0. 前言

最近很多uu们过来问鼠鼠一些 c语言/python 的问题,遂决定开一个答疑帖,方便大家学习交流。但是呢既然开了帖,也就不只讲别人问过的地方,顺便把常见的一些问题都与大家一同学习一遍。又考虑到很多uu跟鼠鼠一样是电脑小白,也顺带分享一些电子产品、生活小知识。

鼠鼠水平有限,内容仅供参考,有错误遗漏之处欢迎斧正。

注意:下文中大部分操作均为免费,有额外开销之处会予以标明。

鼠鼠这博客皮肤渲染代码高亮不太对劲,大家复制到自己的编译器看吧。

1. c/c++ 入门

鼠鼠学习的课程是c语言,指定的编译器是 Embarcadero Dev-Cpp,下文以此为例进行展示。大家可以根据自己课程的要求调整。

(注:OI系列竞赛中习惯使用 devcpp 的同学可以对标 dev,我个人觉得操作没有太大区别)

(注:学习 python 的uu们可以移步其他博客查看攻略,鼠鼠使用的是 pycharm,可作参考)

1.1 下载

em devc++下载

em devc++下载指南

以上,可以从 sourceforge 或 github 上下载 Embarcadero Dev-Cpp。如果使用 devcpp (图标为蓝色的那个),可以使用腾讯下载

1.2 简单配置

刚下载好是这样的。$\uparrow $

  1. 按住 Ctrl 键后鼠标滚轮可以控制右侧编辑区字体大小

  2. 鼠标指针移到左侧项目管理区和右侧编辑区的交接处,按住鼠标左键往左拖,可以隐藏左侧项目管理区。

  3. 上方菜单选择 "工具" \(\rightarrow\) "编辑器选项"

    • "基本"、"显示"、"语法"三项可以改变主题
    • (字体推荐默认的 Consolas)
    • (鼠鼠用的是 obsidian 主题: "基本"-"高亮显示当前行"选择黑色,"语法"-"预设"选择 obsidian)
    • 在"代码"-"缺省源"中输入的代码会自动在所有新建文件中生成,就不用每份代码都打一遍头文件了。
    • "代码补全": "Code Completion"会自动帮你匹配库函数、结构体对象等等,但是会写代码时突然卡手,建议关掉;"完成符号"会自动匹配括号和引号,看个人习惯使用,鼠鼠的只匹配大括号。
    • "自动保存":一定要打开!!!最好间隔调个1分钟。以防你写了一大堆代码之后突然断电/死机没有保存,造成惨剧。
  4. 上方菜单选择 "工具" \(\rightarrow\) "编译选项"

    • "设定编译器配置"最好是 64-bit Release

    • 开启"编译时加入以下命令"并输入以下代码:

    • -Wall -Wextra
      
    • 具体为什么后面说。

搞定了长这样。\(\uparrow\)

1.3 新手常见错误

(转载)前人总结

下表速查:(CE 就是不能编译,warning 能编译但会警告,WA 就是答案错误,RE 就是运行时出错)

错误 结果 解决方案
int mian() CE 缺省源写对
retuen 0; CE 缺省源写对
return 0;忘了写 OI中WA 缺省源写上
分号,括号漏了 CE 看高亮确定配对关系,补上漏的
使用中文分号/逗号 CE(有时本地会通过,交题CE 写代码时一定注意输入法,英文,符号为半角!!!
赋值运算符和 == 不分 warning、WA 一定小心!!记牢c++的=是赋值
运算符优先级错误 warning、WA 不确定就多打括号,人为规定优先级
数组没开够 WA/RE 多开5个肯定就没事了
int溢出 WA 开long long
未初始化局部变量 warning、WA 养成初始化习惯

我们可以看到,上述错误中 CE、warning 的可以当场改过来,减少了无反馈考试考场能过,评测暴毙的可能性。那么 warning 是怎么来的呢?诶,正是编译选项中这两个代码的功劳:

-Wall -Wextra

所以一定要编译选项加上这两句,写代码时把 warning 当作 error 来对待!

对于编译器报告的 error 或 warning,如果看不懂,直接复制后到浏览器搜索,一般都会有人解释过含义,给出解决方法的。

在 OJ 上测试时,一般 RE 代表你的程序中存在数组越界或除数为 0 等情况;TLE 代表运行超时,可能是出现死循环或者程序需要优化运行效率;MLE 则是占用内存过大,需要优化空间复杂度。

1.4 调试与对拍

1.4.1 中间输出调试

其实名字我忘了,乱编了一个

老师会教大家断点调试,但是这样做非常难受,看的人眼花。有没有好一点的调试方法呢?

(转载)中间输出调试

鼠鼠以 B3925 [GESP202312 三级] 小猫分鱼 为例讲解。

看到这个题,鼠鼠对第一组样例进行分析:(两只猫,每轮丢掉一条鱼)

  1. 在只有一只猫时最少这只猫分1条,另一只分相等数量1条,外加丢掉1条,共3条。

  2. 在第二只猫时最少这只猫分3条(因为要留下3条进行1),另一只分相等数量3条,外加丢掉1条,共7条。

答案为7,符合样例1。

综上,鼠鼠分析出此题应该倒推:
每只猫的逻辑都是分 \(n\) 堆,取一堆,扔 \(x\) 个。记第 \(i\) 只猫扔完后还有 \(s_i\) 只鱼,则有:

\[s_i = \dfrac{n-1}{n}(s_{i-1} - x) \]

每只猫都有的吃,\(s_n = 1\) 最小,此时对应的 \(s_0\) 就是答案(海滩上一开始最少的鱼数)了。

反推公式可得

\[s_{i-1} = \dfrac{n}{n-1}s_i + x \]

鼠鼠开心地写出了如下代码:

#include<stdio.h>
#define N 1005

int n,x,s[N];
int main(){
	scanf("%d%d",&n,&x);
	s[n]=1;
	int i;
	for(i=n;i>=1;i--)s[i-1]=s[i]*n/(n-1)+x;
	printf("%d\n",s[0]);
	return 0;
}

结果一看:

样例1通过了,可是样例2输出了7,明显不对。为什么呢?

这时就要用到中间输出调试来查错了。

鼠鼠先在输入 \(n\)\(x\) 后立即输出它们,以检查输入的正确性:

int main(){
	scanf("%d%d",&n,&x);
	printf("orz: %d %d\n",n,x);
	s[n]=1;
	int i;
	for(i=n;i>=1;i--)s[i-1]=s[i]*n/(n-1)+x;
	printf("%d\n",s[0]);
	return 0;
}

运行代码,可以发现输出多了一行:

orz: 3 1

证明输入没有问题。

Tip: 在调试输出时加入特殊信息,如 orz 等,可以帮助快速确定哪行输出是调试输出。

接下来就要检查代码逻辑了。我们在每次算出 \(s_i\) 之后输出 \(s_i\) 看看对不对。代码如下:

int main(){
	scanf("%d%d",&n,&x);
	printf("orz: %d %d\n",n,x);
	s[n]=1;
	int i;
	for(i=n;i>=1;i--){
		s[i-1]=s[i]*n/(n-1)+x;
		printf("sto %d:%d\n",i-1,s[i-1]);
	}
	printf("%d\n",s[0]);
	return 0;
}

调试的输出如下:

sto 2:2
sto 1:4
sto 0:7

这意味着\(s_2=2,s_1=4,s_0=7\)。这对不对呢?我们手动验算一下:

\[s_3=1,s_2=\dfrac32s_3 + 1 =\dfrac52 \]

这时就发现错误了:我们贪心地让 \(s_3=1\),却忽略了这样会使后面的 \(s_i\) 出现小数,这是不符合常理的。

但是我们发现 \(s_3\) 越小 \(s_0\) 显然越小,于是枚举 \(s_n\) 使得每次算出的 \(s_i\) 都是整数即可。更改代码如下:

int main(){
	scanf("%d%d",&n,&x);
	if(n==1)return printf("1\n"),0;
//	printf("orz: %d %d\n",n,x);
	int i;
	for(s[n]=1;;s[n]++){
		for(i=n;i>=1;i--){
			if(s[i]%(n-1))break;
			s[i-1]=s[i]*n/(n-1)+x;
//			printf("sto %d:%d\n",i-1,s[i-1]);
		}
		if(i==0)return printf("%d\n",s[0]),0;
	}
}

其中因为要对 \(n-1\) 取模,特判了 \(n=1\) 的情况。

记得对调试输出注释后提交,准备开香槟咯!!!

愉快地提交代码,我们得到了90分的好成绩。

1.4.2 对拍

为啥错掉了?明明样例都过了啊?

这时我们就要用到一个叫对拍的技巧查错了:

(转载)C++ 对拍详解

首先我们在本题的提交记录里面找到一份可获得100分的AC代码,复制到本地,保存为 ans.c :

#include<stdio.h>
#define N 15

int n,x;
ll s[N];
int main(){
	scanf("%d%d",&n,&x);
	if(n==1)return printf("1\n"),0;
	int i;
	for(s[n]=n-1;;s[n]+=n-1){
		for(i=n;i>=1;i--){
			if(s[i]%(n-1))break;
			s[i-1]=s[i]*n/(n-1)+x;
		}
		if(i==0)return printf("%lld\n",s[0]),0;
	}
}

待debug的90分代码命名为 B3925.c,放在同一个文件夹下备用;

随后编写一个 rand.c 用于随机生成数据(可以先不用管什么意思):

#include<stdio.h>
#include<time.h>
#define fr(i,a,b) for(i=a;i<=b;i++)
#define ull unsigned long long
#define db double
#define mod 998244353
#define N 100005

ull S;
void init(){
	struct timeb tb;
	ftime(&tb);
	S=tb.time*1000+tb.millitm;
//	printf("%llu\n",S);
}
ull rand(){
	S^=S<<13;S^=S>>7;return S^=S<<17;
}

int main(){
	init();
	int n=rand()%9+1;
	int m=rand()%n;
	printf("%d %d\n",n,m);
	return 0;
}

最后编写一个程序 duipai.c 用于对拍(也先不用管什么意思):

#include<stdio.h>
#include<windows.h>
#define fr(i,a,b) for(i=a;i<=b;i++)
#define ll long long
#define db double
#define mod 998244353
#define N 100005


int main(){
	int cnt;
	for(cnt=1;;cnt++){
		system("rand.exe > 1.in");
		system("ans.exe < 1.in > 1.ans");
		system("B3925.exe < 1.in > 1.out");
		if(system("fc 1.ans 1.out"))return puts("WA"),0;
		printf("#%d :AC\n",cnt);
	}
	return 0;
}

四个c程序置于同一个文件夹下,全部编译一遍,最后运行 duipai 程序即可进入对拍。

如图所示,程序运行没多久停了下来,可以看到上面的 #24: AC 和下面的WA,这意味着我们随机的前24组数据中自己的程序和标准程序(下称 std 标程 或 std)运行结果一致,但是第25组数据中自己的程序出现了错误。这时打开文件夹中的 1.in 文件,就获得了一组使得自己的程序出现错误的数据(下称 hack 数据),我们利用它配合中间输出调试法,就能诊断出程序的错误之处。

很快啊,鼠鼠就发现原来是答案太大了,int 不够存储,改成 long long 即可。(1.3 表格倒数第二条)

进而想到答案太大时,程序枚举所有的 \(s_n\) 太慢了,可能运行超时,便改为只枚举 \(n-1\) 的倍数(想想为什么对?),便可以通过此题。AC代码参见 ans.c

强烈建议先前不会对拍的uu们跟着试一下,再来看以下的解析。


下面我们来详细地解释一下以上程序的原理。

还记得为什么对拍吗?鼠鼠写的代码通过了样例,提交代码却有部分错误。想要hack数据却不知道从何找起,人工构建数据效率又太慢。

这时我们想到,如果我们能(1)找到一份标准代码,它不会出错;(2)再写一个程序随机生成数据;那么不就可以做一个循环,每次随机生成输入数据,分别用自己的代码与标准代码求出答案,再比对答案,若一致则重新生成数据,不一致则保存此 hack 数据,随后退出循环进行人工调试,是不是就方便很多了? 诶,这正是计算机能做到的事情嘛~

上述流程正是 duipai.c 的描述,其中(1)对应 stdans.c,(2)对应rand.c

使用中间输出调试以及对拍进行 debug 的流程图总结如下:

flowchart TD; start([Start]) init[写好自己的代码] success{程序正确通过题目} thrusample{通过样例} hvhack{有 hack 数据} debug[中间输出调试] duipai[对拍] myend([End]) start-->init-->success-- Yes -->myend success-- No -->thrusample thrusample-- Yes -->hvhack thrusample-- No -->debug debug --solve the problems-->success hvhack-- Yes -->debug hvhack-- No -->duipai duipai-- until find the hack data -->debug

可以清晰地看出中间输出调试(或者其他调试方法)是 debug 的核心步骤,而对拍保证了你不会没有数据用于调试。

另外关于 system 命令操作,程序中只用到了调用 exe、文件读写以及 fc 文件比对三个命令,较为简单故不作解释,大家自行查阅资料即可。

而关于 rand.c 随机程序,生成随机数的思路大概就是取一个时间作为随机种子,再通过随机算法生成随机数即可。

(转载)c语言获取当前时间精确到毫秒

(转载)xorshift算法生成随机数的原理是什么?

实在不懂的可以先抄鼠鼠的代码,main函数中调用rand()就能生成 unsigned long long 类型的随机数了。如果要生成特定条件的数据,比如要求在某个区间内/生成树与图等等,可以自行查阅资料。

对拍是非常强而有力的查错手段,尤其是考试时不能收到即时反馈的条件下。大家可以学起来~

1.5 c++ 转 c 注意

c++ 中很多操作 c 中没有,需要额外注意。包括但不限于:

  1. for循环起始时定义变量
  2. namespace
  3. swap,sort(c 中不太相同)
  4. cin,cout
  5. struct中定义函数(另外 c 中定义 struct 变量时要带上 struct 关键字,比如struct node A;)

1.6 常见网站分享

这里与2和3中的有重叠,先于此汇总,2和3中相同的会放个 reference。
汇总


编程类:

洛谷 是一个综合性的编程社区,从入门到进阶都有。建议大家都去开个号,可以做一下官方题单,对入门很有帮助。

acwing同理,每周有周赛适合小白玩玩。

牛客是偏向大学 ACM 竞赛的。

atcodercodeforces 都是进阶算法竞赛的优质比赛网站,以个人赛制为主,大家可以在有一定算法基础后尝试 at 的 ABC 和 cf 的 div3 难度。


工具类:

deepl翻译 相对较准的翻译

bing 鼠鼠最爱的搜索引擎,搭配 chrome 或 edge 浏览器更香~

oiwiki 算法竞赛百科,当然也可以查阅一些编程基础问题

oeis 整数数列大全,找规律利器

markdown官网 markdown 语法的教程,还有在线编辑器

比如这篇文章就是用 markdown 写的,这种语法可以插入代码、表格、超链接、数学公式甚至化学方程式、外链播放器等等,非常实用。

typora markdown 编辑软件,但是要钱

sublime 查看 txt 文本很方便

krita 画图草稿(虽说原本好像是画画用的)

VSC 支持插件的编译器,用好了很强大(虽说鼠鼠只喜欢用它看 txt 文件,比写字板好看qwq)

utools 可以在本地安装插件应用,十分好用。鼠鼠比较喜欢本地文件查找的那个插件,比电脑自带的快很多。也有 markdown 编辑器等应用。


社区类:
开发者社区比如 cnblogs 博客园CSDN;问编程问题答复比较准确。本文正是发布在博客园。

知乎 或者 贴吧 也是比较常见的问答社区。

2. 电脑手机知识补充

2.1 快捷键

(转载)快捷键

像是什么ctrl+ACVYZ,alt+tab/F4,任务管理器,cmd之类的总得会吧。。

2.2 steam下载

上网搜索一下就有很多教程了,鼠鼠简单说一个:
下载uu加速器 并加速 steam商店;

进入steam官网下载即可。

uu们需要谨慎甄别,一般而言不开加速器就可以正常购买游戏的就是假的。(本地游玩已经下载好的不需要加速器)

(转载)一些分别真假steam的方法

2.3 手机NFC功能

鼠鼠根据身边调查法发现还有很多人不知道NFC功能(包括鼠鼠自己),所以稍微科普一下。

百度百科: 近场通信(Near Field Communication,简称NFC),是一种新兴的技术,使用了NFC技术的设备(例如移动电话)可以在彼此靠近的情况下进行数据交换,是由非接触式射频识别(RFID)及互连互通技术整合演变而来的,通过在单一芯片上集成感应式读卡器、感应式卡片和点对点通信的功能,利用移动终端实现移动支付、电子票务、门禁、移动身份识别、防伪等应用。

设置中搜索 NFC,开启即可。

  1. 校园卡:直接用手机的NFC感应区贴紧校卡,跟随手机指引完成读卡即可。搞定之后把手机想象成卡,刷 pos 机和刷门禁都行。鼠鼠认为比人脸/二维码支付方便(有快捷调出方式,刷卡灵敏,无需联网)。
  2. 交通卡:在"卡包"中搜索本地交通卡绑定即可。地铁公交都能用~
  3. 同品牌设备互传文件。

3. 生活小寄巧

3.1 自行车购买

鼠鼠建议不要贪便宜就买个三四百的,也不用追求性能非得买很高端的,可以结合预算在贴吧的自行车吧问一问,多收集一些信息再决定。

没开过上路的新手uu推荐买山地车,更稳,不易摔车。

3.2 笔记本购买

买笔记本可以在贴吧的笔记本吧问一问再决定,有运行代码需求的可以考虑游戏本。

装电脑的可以在贴吧的图拉丁吧问一问,千万看准了自己买,不要被坑着买了工业垃圾/二手废料!可以吧里多看看别人被骗的经历,引以为戒。

以上意见仅供参考,望自行斟酌。

希望uu们大学生活快乐!国庆放假快乐!

(一稿完成时间:2024/9/28 16:00)

(updated at:2024/11/15 8:53,为参加相关比赛而作些许修改,主要改动为总结对拍调试流程图)

posted @ 2024-09-27 23:48  Vizing  阅读(91)  评论(4编辑  收藏  举报