程序设计实践考试的入门模板

这个博客不再更新,新博客地址请戳

程序设计实践考试的入门模板

前言

其实从大二开始就在整理有关如何学习C语言以及如何应对程序设计实践(和C语言考试)的经验和相关模板,由于各种原因,这件事情也没有一个很好的进展。前不久邹大佬提起这事儿的时候,突然觉得是应该好好整理一份类似于参考资料的东西了。

我打算先由自己整理出来这份模板,主要面向应对程序设计实践考试的同学。
本文当中可能会存在一些错误和遗漏的东西,还请指正。(email 1278683056@qq.com

使用这份模板之前,你需要学会最基本的C语言(C++)语法,所以关于语法部分如果还不是很熟悉,这份模板对你而言没有任何帮助。

在信工院程设挂科率奇高的大环境下,我觉得整理出一份适合于入门者使用的模板很有必要,希望能够帮助到大家。


第一章 关于程序设计入门

- 1.online judge

oj指的是在线评测系统,程序设计实践考试在oj上进行,所以首先我们需要对oj有一个大致的了解。

1.1 根据测试,xtuoj 1秒钟大约能够运行3e7次,这一点在避免得到TLE很重要,学会计算时间复杂度和空间复杂度是数据结构课程的内容,在此不赘述。

1.2 介绍几种常见错误的原因,以便于对症下药。

类型 原因 解决方案
WA(答案错误) 程序输出跟标程输出不一致,算法设计上有错误,或存在逻辑错误 改进算法,检查逻辑问题
TLE(超时) 程序未能在限定时间内结束,算法复杂度过高,或存在死循环 检查是否存在死循环,判断算法时间复杂度是否可行,如果确认复杂度可行,有可能是被卡常
RE(运行错误) 除0,栈溢出,内存访问越界等 ①检查除法运算的地方是不是除0了 ②如果使用了递归算法,判断是不是爆栈了 ③ 下标超过范围,数组开小,会访问越界
MLE(内存超限) 申请的内存超过了题目限制范围,一般是数组开大了,也可能是因为在死循环里不停地申请内存 改进算法,计算空间复杂度
PE(格式错误) 答案对了,但是输出格式有问题 仔细检查换行,空格等问题,距离AC很接近了

在此解释一下何为卡常
卡常指的是,程序算法本身复杂度符合题目要求,按理说是能够AC的,但可能由于自己代码写了很多不必要的东西,导致超时。当然,不排除会有出题人故意卡常。解决方法是尽量避免不必要的额外运算,另外,在输入输出上能通过使用外挂从而加速运行。外挂会在接下来的模板中给大家贴出。

何为爆栈:
递归层数太多,导致栈溢出。(这类似于死循环,但是程序还没超时就因为爆栈而终止运行了。)如果确实是因为层数太多,也可以手动模拟栈(stack),或者改为队列(queue)。

- 2.分析题型

程设考试一般6题,对于绝大多数人而言,通过2题意味着考试及格,当然也有少部分人可以1题及格。

一:暴力,所谓的签到题
二:执行
三:贪心
四:模拟
五:数据结构
六:图论
七:动态规划
八:数学相关

对于以上题型,一到四项没有什么很好的模板可供参考,更多的是平时的积累和练习,然而在考试时这些题相对后面的题型来说,属于简单题;针对五到八项,接下来我会整理出一些适合的模板。


第二章 数学相关

- 1 素数相关

1.1单个数n的判定,时间复杂度O(sqrt(N))

bool isprime(int n){
	if(n<2)return 0;
	if(n<4)return 1;
	for(int i=2;i*i<=n;i++){
		if(n%i==0)return 0;
	}
	return 1;
}

解释:
素数的因子只有1和它本身,那么如果从1到sqrt(n)都没有数字是n的因子,那么n一定是质数。
可以发现,一个数的所有因子,一定均等地分布在sqrt(n)的左右两边。
比如数字9的因子{1,3,9},左边是{1,3},右边是{3,9}。

1.2素数表,时间复杂度O(N)

const int maxn = 1e5+10;
bool notprime[maxn];
void getprime(){
	notprime[0]=notprime[1]=1;
	for(int i=2;i<maxn;i++){
		if(notprime[i]==0){
			for(long long j=1LL*i*i;j<maxn;j+=i){
				notprime[j]=1;	
			}
		}
	}
}

解释:
notprime[i]==1表示i不是素数,反之表示i是素数。
对于一个素数a,它的倍数一定都不是素数,所以我们可以对于遇见的每个素数,都把它的倍数标记为非素数,以上代码就是实现这一过程的。
由于i*i可能会溢出,为了避免溢出,j使用long long型。j从i^2开始,因为小于i倍的部分都已经被修改过了,不需要重复修改。

1.3 合数分解(值域为int的)
把一个合数a分解为 a = 1 * p1^x1 * p2^x2 * … *pn^xn 的形式

const int maxn = 1e5;
int p[100],x[100];
void getheshu(int n){
	int cnt=0;
	for(int i=2;i<maxn&&n>1;i++){
		if(n%i==0){
			p[++cnt]=i;
			while(n%i==0){
				n/=i;
				x[cnt]++;
			}
		}
	}
	if(n>1){
		p[++cnt]=n;
		x[cnt]=1;
	}
}

解释:
调用这个函数后,n的分解结果存储在p数组和x数组中,表达形式如上述。
如果能够分解出一个质数p,那么循环分解出的p的最高次幂。
最后剩下的“尾巴”如果大于1,说明这个数字一定是个质数。

- 2 最大公约数

gcd和lcm
最大公约数主要用到的是辗转相除法

int gcd(int a,int b){
	int c;
	while(b){
		c=a;
		a=b;
		b=t%b;
	}return a;
}

当然我们可以直接使用库函数__gcd(,)它是内部已经实现好了的函数,所以可以省去上面的代码,请注意该函数前面有两条下划线。

至于a和b的最小公倍数,等于a*b/gcd(a,b)
我们可以实现函数:

int lcm(int a,int b){
	return 1LL*a*b/gcd(a,b);//避免32位整型溢出
}

- 3 组合数

3.1组合数打表

对于较小的组合数,我们一般采用打表的方式存储答案,主要有以下两种方法:

int dp[30][30];
for(int i=0;i<30;i++){
	dp[i][0]=dp[0][i]=1;
}
for(int i=1;i<30;i++){
	for(int j=1;j<30;j++){
		dp[i][j]=dp[i-1][j]+dp[i][j-1];
	}
}
/**解释:dp[i][j]表示从i+j个物品中选择i个物品,不选择j个物品,
那么它可以由dp[i-1][j]和dp[i][j-1]转移得到,满足加法定理。
C(n,k)对应dp[n-k][k]
**/

第一种方法是我喜欢的写法,不过以下第二种方法可能更加方便。

int dp[30][30];
for(int i=0;i<30;i++){
	c[i][0]=c[i][i]=1;
	for(int j=1;j<i;j++){
		dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
	}
}
/**解释:这种写法的C(n,k)对应的值是dp[n][k]
**/

以上打表的算法,时间复杂度都是O(n^2)的,所以当复杂度过高时,请使用卢卡斯定理。
另外,根据数据范围调整32位整型和64位整型,如果要求取模,记得每次运算都要取模。

下面我们介绍卢卡斯定理。

3.2 卢卡斯定理

具体原理可以自行百度学习,这里还牵涉到了乘法逆元的知识点,初学者不妨把它当作黑箱子来使用。

typedef long long ll;
const int maxn = 1e5+10;
const int mod  = 1e9+7;

ll qpow(ll a,ll n){
    ll ret=1;
    while(n){
        if(n&1)ret=ret*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ret;
}

//
  • 21
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
目 录 译者序 前言 第1章 风格 1 1.1 名字 2 1.2 表达式和语句 4 1.3 一致性和习惯用法 8 1.4 函数宏 14 1.5 神秘的数 15 1.6 注释 18 1.7 为何对此费心 22 第2章 算法与数据结构 23 2.1 检索 23 2.2 排序 25 2.3 库 27 2.4 一个Java快速排序 29 2.5 大O记法 31 2.6 可增长数组 33 2.7 表 35 2.8 树 39 2.9 散列表 43 2.10 小结 46 第3章 设计与实现 48 3.1 马尔可夫链算法 48 3.2 数据结构的选择 50 3.3 在C中构造数据结构 51 3.4 生成输出 54 3.5 Java 56 3.6 C++ 59 3.7 Awk和Perl 61 3.8 性能 63 3.9 经验教训 64 第4章 界面 67 4.1 逗号分隔的值 67 4.2 一个原型库 69 4.3 为别人用的库 72 4.4 C++实现 79 4.5 界面原则 82 4.6 资源管理 84 4.7 终止、重试或失败 86 4.8 用户界面 90 第5章 排错 93 5.1 排错系统 94 5.2 好线索,简单错误 95 5.3 无线索,难办的错误 98 5.4 最后的手段 101 5.5 不可重现的错误 103 5.6 排错工具 105 5.7 其他人的程序错误 107 5.8 小结 108 第6章 测试 110 6.1 在编码过程中测试 110 6.2 系统化测试 114 6.3 测试自动化 118 6.4 测试台 120 6.5 应力测试 123 6.6 测试秘诀 125 6.7 谁来测试 126 6.8 测试马尔可夫程序 127 6.9 小结 129 第7章 性能 130 7.1 瓶颈 130 7.2 计时和轮廓 135 7.3 加速策略 138 7.4 代码调整 140 7.5 空间效率 144 7.6 估计 145 7.7 小结 147 第8章 可移植性 149 8.1 语言 149 8.2 头文件和库 154 8.3 程序组织 156 8.4 隔离 159 8.5 数据交换 160 8.6 字节序 161 8.7 可移植性和升级 164 8.8 国际化 165 8.9 小结 167 第9章 记法 169 9.1 数据格式 169 9.2 正则表达式 174 9.3 可编程工具 180 9.4 解释器、编译器和虚拟机 182 9.5 写程序的程序 186 9.6 用宏生成代码 189 9.7 运行中编译 190 后记 195 附录:规则汇编 197 索引 200

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值