未名湖边的烦恼-蓝桥杯算法训练题-递推/递归

问题描述
每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩。
每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个。现在的问题是,这些人有多少种排法,可以避免出现体育组没有冰鞋可租的尴尬场面。(两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种排法)

输入格式
两个整数,表示m和n
输出格式
一个整数,表示队伍的排法的方案数。
样例输入
3 2
样例输出
5
数据规模和约定
m,n∈[0,18]
问题分析

分析:f(m, n)表示m人还鞋,n人租鞋的情况下排序种数
1、如果 m < n 还鞋的如果比租鞋的少,那肯定无解 return 0;
2、如果 n == 0 鞋没人租 那肯定就一个解 全是还鞋的 return 1;
排除了 m < n 和 n == 0 的情况,递推过程如下: f(m, n) -> f(m – 1, n) + f(m, n – 1) -> …- > f(5, 1) + f(4, 2) + f(3, 3) -> f(4, 1) + f(3, 2) -> f(3, 1) + f (2, 2) -> f(2, 1) -> f (1, 1)

 

《大话数据结构》读书笔记

【链表】单链表要查找某一个结点时,需要从头结点开始遍历整个链表
当想要从当中一个结点开始遍历整个链表的时候,这是原来的单链表结构解决不了的问题,所以可以用循环链表解决问题
当想要反向遍历整个链表的时候,这是原来的单链表和循环链表解决不了的问题,所以可以用双向链表来解决。正反都可以遍历了
静态链表:便于在不设『指针』类型的高级程序设计语言中使用链表结构。
【栈】1.word、photoshop等动作的撤销键用的是栈
2.web中的后退键
3.两栈共享空间:如果两个人分别一居室各有各的卫生间和厨房客厅,显然空间利用率不高,但是如果改成两居室各有各的房间但是共享卫生间和厨房客厅,这样的空间利用率就显著提高了。 如果我们有两个相同类型的栈,我们为它们各自开辟数组空间,可能一个已经满了但是还有一个还有很多存储空间。我们完全可以用一个数组来存储两个栈,分别放在数组的两个端点处,如果两个栈增加元素,数据从两个端点向中间延伸。{主要适用于两个栈的空间需求有相反关系的时候,也就是一个栈增长时另一个栈在缩短的情况,就像比如买卖股票,一个人买必定是有一个人做出了卖出的操作,所以这样就可以用两栈共享空间存储方法来存储}
4.顺序栈和链栈:虽然在时间复杂度上都是一样的,但是空间性能来说,顺序栈需要事先确定一个固定的长度,可能会存在空间浪费的问题,优势是存取时定位方便,而链栈则要求每一个元素都有指针域,总加了一些内存的开销,对于栈的长度无限制。所以,如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些。
!括号匹配的问题很符合栈的先进后出,后进先出的特点
5.栈的应用:递归。递归这种存储了某些数据,并在后面又以存储的逆序恢复这些数据以提供之后使用的需求,这很符合栈的数据结构。
6.栈的应用:四则运算表达式求值:将中缀表达式转化为后缀表达式。规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素一次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。
【队列】操作系统和客服系统、银行排号系统等都是应用了队列功能(排队)。先进先出(FIFO)
循环队列:队列头尾相接的顺序存储结构称为循环队列。
【串】由零个或多个字符组成的有限序列,又名字符串。
“over”“end”“lie”可以认为是“lover”“friend”“believe”的子串。
1.电子词典查找单词实现的原理就是字符串这种数据结构的典型应用。
2.串的比较相当于我们查纸质字典的过程。
3.对于串的顺序存储,有一些变化,串值的存储空间可在程序执行过程中动态分配而得。比如在计算机中存在一个自由存储区,叫做“堆”。这个堆可由C语言的动态分配函数malloc()和free()来管理。
4.串的链式存储结构除了在连接串与串操作时有一定的方便之外,总的来说不如顺序存储灵活,性能也不如顺序存储结构好。
5.串的模式分配:(即为子串的定位操作), 比如说查找一个单词(相当于子串)在一篇文章(相当于一个大字符串)中的定位问题。 是串中最重要的操作之一。
{朴素的模式匹配算法}:
方法:对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。对主串做大循环,每一个字符开头做T长度的小循环,直到配对成功或全部遍历完成为止。
{KMP模式匹配算法}:
是朴素的模式匹配算法上的提高,大大避免重复遍历的情况。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。
以及KMP模式匹配算法的改进。index函数用于模式匹配,t是模式串,s是原串。返回模式串的位置,找不到则返回0。
6.相对于线性表关注一个个元素来说,我们对串这种结构更多的是关注它的子串应用问题,如查找、替换等操作。
7.所谓回文,就是一个字串的逆转显示,我们只要在串的抽象数据类型中增加一种逆转reverse的操作,就可以实现这样的功能。
【树】
1.前面所讲的栈、队列、串都是一对一的线性结构,而树是一对多的数据结构(树结构)。
2.子树的个数没有限制,但是它们一定是互不相交的。
3.双亲表示法:每个结点除了知道自己是谁之外(data数据域),还知道它的双亲在哪里(parent指针域)。双亲表示法还可以改进,再加一个长子域或者兄弟域
4.孩子表示法:把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。链表+顺序表 链表=child数据域+next指针域;顺序表=data数据域+firstchild头指针域。
这个可以改进为双亲孩子表示法,在顺序表的data和firstchild中间加一个parent域。
5.孩子兄弟表示法:data数据域+firstchild指针域+rightsib指针域。
【二叉树】
1.折半查找算法
2.二叉树的左子树和右边子树是有顺序的,次序不能颠倒。即使树中某结点只有一颗子树,也要区分它是左子树还是右子树。
3.斜树。所有结点都只有左(右)子树的二叉树叫做左(右)斜树。线性表结构就可以理解为树的一种极其特殊的表现形式。
4,二叉树的顺序存储结构:为了避免存储空间的浪费,顺序存储结构一般只用于完全二叉树。
5.二叉链表:lchild指针域+data数据域+rchild指针域
6.就像高考填志愿面临选择哪个城市、哪所大学、具体专业,由于选择方式不同,遍历的次序就完全不同了
7.前序遍历、中序遍历、后序遍历、层序遍历:研究遍历的方法,可以把树中的结点变成某种意义的线性序列,这就给程序通过循环、判断等方式处理实现来提供方便。不同的遍历提供了对结点依次处理的不同方式。
8.线索二叉树:充分利用二叉树中的空指针域,把lchild指向前驱,rchild指向后继。这种指向前驱和后继的指针称为线索加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树。lchild+ltag+data+rtag+rchild 线索化的过程就是在遍历的过程中修改空指针的过程~
9.【赫夫曼树】压缩文件技术:把要压缩的文本进行重新编码,以减少不必要的空间,最基本的压缩编码方法——赫夫曼编码。
带权路径长度WPL最小的二叉树称做赫夫曼树(最优二叉树)。
将有权值的叶子结点按照从小到大的顺序排成一个有序列,取头两个最小权值的结点作为一个新结点N1的两个子结点,则N2的权值为15.将N1替换A与E,插入有序序列中
然后N1与B作为新结点N2的两个子结点插入到有序序列中……
【赫夫曼编码】解决当年远距离通信(电报)的数据传输的最优化问题
要设计长短不等的编码,则必须是任一字符的编码都不是另一个字符的前缀,这种编码称作前缀编码~
10.图的存储结构中比较重要的是邻接矩阵和临界表,分别代表着边集使用数组还是链表的方式存储。十字链表是邻接矩阵的一种升级,而邻接多重表则是邻接表的升级。边集数组更多考虑的是对边的关注。通常稠密图,或存储数据较多,结构修改较少的图,用邻接矩阵要更合适,反之则应该考虑邻接表。

去除NSLog时间戳及其他输出信息

如果不想看见NSLog的时间戳以及其他输出信息,我们可以在前面自行添加宏定义

这样就会只出现要输出的内容,去除前面的时间戳和其他格式:

11111


更多的时候我们可以把它改成这样的宏定义:

这样我们打印的内容如下:
830DD49D-1CE7-4274-87E8-0E7FC5607B20

这样输出信息会出现对应输出语句的“文件名:行号”。

锁定Mac的键盘:连击5次option键

打开【系统偏好设置】 -> 【辅助功能】 -> 在侧边栏的【互动】项目下选择【鼠标与触控板】 -> 点击右侧的【选项…】 -> 【按下option键五次来开关鼠标键】打勾 -> 【好】
解锁Mac键盘锁只需再重新连击5次option键即可。

Snip20151218_3
Snip20151218_4

Snip20151218_5

【C++ 与 STL】双端队列:deque

头文件 #include <deque>

常用方法:
a.push_front(0);//在头部加入数据0
a.push_back(11);//在尾部加入数据11
a.pop_front();//在头部删除数据
a.pop_back();//在尾部删除数据
a.resize(num);//重新指定队列的长度
a.size() //返回容器中实际数据个数
a.max_size() //返回容器中最大数据的数量

测试代码:

输入输出:
1 2 3 4 5 6 7 8 9 10
在头部加入数据0:
0 1 2 3 4 5 6 7 8 9 10
在尾部加入数据11:
0 1 2 3 4 5 6 7 8 9 10 11
在头部删除数据:
1 2 3 4 5 6 7 8 9 10 11
在尾部删除数据:
1 2 3 4 5 6 7 8 9 10
a.size() = 10
1 2 3 4 5 6 7 8 9 10 0
a.empty() = 0
a.max_size() = 4611686018427387903