还在为不知道怎么写CPL课程的小游戏项目而烦恼吗?
那可真是太巧了!我这恰好一不小心花了点时间写了一个小项目:
你猜怎么着!恰好完成了其中一道期末项目的要求!
我将代码都留了下来,并且编写了一段较为详尽的教程来告诉你我是怎么写出这个项目的。
我把项目放在 GitHub 这里了!要是真的不知道大项目该怎么写,那你也来看看吧!
还在为不知道怎么写CPL课程的小游戏项目而烦恼吗?
那可真是太巧了!我这恰好一不小心花了点时间写了一个小项目:
你猜怎么着!恰好完成了其中一道期末项目的要求!
我将代码都留了下来,并且编写了一段较为详尽的教程来告诉你我是怎么写出这个项目的。
我把项目放在 GitHub 这里了!要是真的不知道大项目该怎么写,那你也来看看吧!
XMake
是由国人开发的基于 Lua
的 C/C++
构建系统,支持多重编译器和平台,利用 Lua
轻量、简洁、灵活、跨平台等特性,带来不一样的构建体验。
请注意:不建议使用除 vscode
以外的编辑器使用 XMake
编写项目,这些编辑器有 XMake
插件但并不好用,没有办法提供合适的 include
解析,导致编写代码十分的困难,好在我们现在仅仅是借助 XMake
来帮助我们打包项目,在这里我们以 SDL2
为例,但 XMake
不仅限于此,它有十分充足的仓库,你几乎能在里面找到所有你想要使用的 C/C++
库。
大家可能在做 CQ's Challenge
的时候发现利用 getchar()
读掉换行符的方式(这在平时 OJ 上都是非常有效的)出现了意想不到的问题,样例在本地跑的是对的,但是交上去就不大对劲,这到底是什么情况?
是这样的,某 CQ 在 Windows
下出题,一开始并没有考虑到这个问题,所以使用了自己用 C++ 编写的随机数程序,并直接在 Windows
平台的环境下自动生成数据。
那在 Windows
平台下生成数据会有什么区别呢?
随着题目难度的增大,某 CQ 发现开始有越来越多的同学对空间超限以及时间超限发出了疑问。
某 CQ 十分的奇怪:明明题目的时空限制放的都很宽啊,怎么会出现超时超空间的问题呢?
一问发现有些同学的算法不是很好,导致在一些数据范围比较大的题目当中甚至出现了三重循环,这你不超时谁超时?如果看上去并不会超但是却时间超限了,那往往是死循环导致的。
那么我们到底该怎么样控制我们程序的时空复杂度呢?
其实十分的简单!下面就来大致的讲一讲罢!
时间复杂度:
这个时候你可以盯着题目给定的数据范围看,看看 n 的最大可以到多少,还有一些其它的数据范围,当你想好你将会怎么来做这道题之后,你就可以通过这些东西来计算自己应该用哪种数据类型(int
-> long long
)
这次我们介绍一个很方便但是在 C 语言当中依然用起来有些难度的东西——动态数组。
行向量 vector
,在 C++STL
当中有它,但我们更习惯称它位动态数组,它的特点就是能够动态分配数组的内存,方便我们应对未知数据量的问题。
这么好用?不,它虽然优化了空间,但它也用掉了一部分的时间用来维护这个动态数组。不过总体上来说,这个动态数组还是很推荐学一学,用一用的。
(有些人特别钟爱用 vector
存图,时间常数大的起飞,我不说是谁(︶^︶))
接下来就大概的看一看实现的代码,相信加上注释和清晰的变量命名,你们应该能看懂。
链表在需要节省内存空间,同时不需要进行随机寻址操作时(也就是使用数组下标那种方式快速访问其中的任意一个元素),是非常优秀的数据结构。
要想看懂它,实际上一段代码就足够:
#include <stdio.h>
#include <stdlib.h>
// 链表的特点在于它占用的内存不像数组那样是连续的,
// 这也正导致了链表需要更多的内存用来记录与某一个节点相连的其他节点,且不能直接调用链表当中某个元素的值
// 但是链表的好处在于它的内存是动态的,而且从头到尾顺序访问并不受影响
// 单独链表的应用范围,抛开没有不谈,还是有一点的
// 比如说某些非逼你需要动态维护数组大小的(我不说是哪道题,自己心里清楚
// 单个链表节点,这里演示的是两个方向都能查找的链表
typedef struct list_node list_node_t;
struct list_node {
int data;
list_node_t *prev, *next;
};
// 在pos指向的元素之后添加一个新的元素
void add(list_node_t *pos, int data) {
list_node_t *node = malloc(sizeof(list_node_t));
node->prev = pos;
node->next = pos->next;
if (pos->next != NULL)
pos->next->prev = node;
pos->next = node;
node->data = data;
}
// 删除pos指向的元素
void del(list_node_t *pos) {
if (pos->prev != NULL)
pos->prev->next = pos->next;
if (pos->next != NULL)
pos->next->prev = pos->prev;
free(pos);
}
int main() {
// 新建一个节点指向链表的开头以方便链表的遍历和添加删除。
list_node_t *list = malloc(sizeof(list_node_t));
list->prev = NULL;
list->next = NULL;
list->data = 114514;
return 0;
}
// 代码未经检查,如果有误自己去改,意思到了(
这一章的内容实际上在题单《逃不掉的语法小技巧 指针》当中已经讲的差不多了,不过我们在这里还需要补充一些东西。
在上一次介绍中我们在文末说到了这样一段话:
顺带一提,我们知道数组的名字就是指向它第一个元素的指针,那么数组到底是什么意思呢?
原来,数组后面方括号里面的数字实际上指的是相对于这个数组指针在内存上的偏移值,偏移 0 那就是第一个元素,偏移 1 那就是第二个元素,以此类推。
由这句话我们知道了数组名字是个指针,而且它实际上指向了数组的第一个元素。
我们都清楚这次的 OJ 十分的阴间,不允许使用 []
来调用数组的某一位,甚至不让用 []
开数组。
特别注意:P2383 是最难的题,需要考虑大量的剪枝优化,建议放到最后做
搜索的题目一开始做会有一点点难理解,练多了才会好,所以这次题目难度变化不会很大,但是数量增加(不过貌似也不是特别多的亚子)
P1433 不要听题解瞎说,正常做数据范围应该是能过的
这里到处都是重点,请各位同学一定要看到最后。
各位好啊!又是我,某 CQ!
发现大家递归好像做的还行了,但是好像基于递归来暴力求解一些问题还不太熟练。
这次我们将从全排列问题开始讲起,将基于递归的搜索的通用做法来给大家过一遍。
将 1 到 n 这 n 的数字按某种顺序排好,一共有多少种排法?并且我们要求你把所有的排法情况按照字典序输出。
data-types 中提到了一些更进阶的数据类型定义方式,在这里某 CQ 将一些内容重述,以便于更好地理解。
C 语言不是一种面向对象语言,因此,它没有对象的概念,但是结构体如果用的好的话,也能够媲美对象。
struct
结构体是一种复合类型,它将多个不同类型的变量组合在一起,形成一个整体。
struct
类型的变量在定义时,会分配一段连续的内存空间,其中包含内部所有字段的空间。
在具体使用的时候,你需要先定义结构体类型及其成员,向编译器告知,你创建了这样一个类型,然后才可以使用这个类型来定义变量:
指针是 C 语言的核心技巧,通过指针,我们可以实现很多高级功能,比如动态内存分配、函数指针、回调函数等等。但是指针也是 C 语言中最容易出错的地方,因为指针的操作非常复杂,一不小心就会出错。本文将介绍一些指针的语法小技巧,帮助大家更好地理解和掌握指针。
取地址:
当我们想要获取某个变量的地址,首先找到这个变量的变量名,我们假设它是 a,如果你在这个变量名前面加上&
(&a
),那么恭喜你,你成功的取出了 a 这个变量的地址。
特别地,如果是一个数组,那么它的数组名就是指向它第一个元素的地址,这也是为什么 scanf
数组的时候不要加 &