NOIP复赛算法复习



【递归算法】【递推】递归是在学完基础语言过后的第一个“算法”,哎,说白了就是函数调用自身,想当初完全无法理解,有了递归,斐波那契啊,汉诺塔啊都很简单了,这也为后面打下了基础,当然,纯递归也很容易超时,没办法~于是就有了记忆化递归,递推嘛差不多,只是用了for循环,加上数组来算,举个栗子:

[cpp] view plain copy

  1. <span style=”font-family:SimHei;”>/*
  2. 记忆化递归解决斐波那契数列
  3. */
  4. #include<cstdio>
  5. long long a[92];//经测试longlong最多能装下第91个数
  6. long long x(int k)
  7. {
  8.     if(k<3)
  9.         return k;
  10.     if(a[k]==0)
  11.         a[k]=x(k-1)+x(k-2);
  12.     return a[k];
  13. }
  14. int main()
  15. {
  16.     int n;
  17.     scanf(“%d”,&n);
  18.     printf(“%I64d”,x(n-1));
  19. }
  20. </span>

 

#——————————————————————————–#

 

【回溯】【DFS(深搜)】这种算法就是建立在递归之上的,大体思路就是:找到最深处(冲啊!!!),返回,找第二种,再返回……直到找完,解决得了很多迷宫问题(你最好不要用此算法尝试数据大得恶心的最优解题,不然超时超得你透心凉心飞扬),顺便刷刷阅读量:

【NOI OJ】8783 单词接龙

【NOI OJ】1818 红与黑

更多搜索题目请点击题库

 

#——————————————————————————–#

【BFS(广搜)】

【BFS】广度优先搜索&【DFS】宽度优先搜索

#——————————————————————————–#

【枚举】世上最强骗分技巧,传说中的暴搜就是它了,任何题都可以枚举,依靠计算机那每秒10亿次的超级运算能力,我们每次都能完美地——超时,当然也有题完全可以枚举,事实上只要你有超过3个for循环,基本上是超时了,题型:

【NOIP普及组】2016年模拟考试(11.5)——火柴棒等式

事实上枚举过了很多题,都水得不想写了……╮(╯▽╰)╭,题库

#——————————————————————————–#

【贪心】最好理解的一种较高级算法,不停地找最大的(真是贪婪),最简单的:一个数字矩阵,从每行选一个数,使其和最大。就选每行最大的即可,题型:

【NOI OJ】1797 金银岛

【NOI OJ】3528 最小新整数

更多贪心题目请点击题库

#——————————————————————————–#

【DP(动态规划)】和递推很类似(只是类似而已),主要是满足一下条件可以尝试:

1.数据规模不大;

2.像贪心,又没办法贪的(反正就是很恶心的,选最大的不行的题);

3.满足无后效性原则:当前的最优解与后面无关,只与前面有关;

4.满足最优化原理(最优子结构性质):从起始到结束的最优路径中的每个节点都为当前最优;

说这么多,到底怎么“规划”呢?

首先划分阶段,使每一个阶段与前面建立起联系;

然后写出状态转移方程(最难的一步),说白了就是将当前最优解用之前的表示出来,这是一个漫长的过程,熟了一下就行,不熟绞尽脑汁也不知道;

接着需要寻找边界,你总不可能让它无休止地找吧……当然有最后就又开始,一定要注意初始化;

最后就是程序的实现了,这个非常简单,不多说。

题型:

【NOI OJ】4977 怪盗基德的滑翔翼

【NOI OJ】2728 摘花生

更多题目请点击题库

#——————————————————————————–#

【图】

一大波文字来袭,做好心理准备……

首先,图是什么?图就是图,请注意,图就是图,请记住,图就是图!好吧,将点用边连起来的就是图,如下,事实上,图是一种数据结构,但我还是归在了算法~

然后,有几个概念(天哪噜,又有概念了,肿么办!!)

【有向】很好理解,点1可以到点2不代表点2可以到点1就是有向。

【无向】相反,点1可以到点2便代表点2可以到点1。

【权值】一条边的大小(也可能是费用、长度什么的)叫权值。

【结点的度】以此点为终点的边的数目。

【结点的出度】简单点,此点可以出去的边的数目。

【结点的入度】反之,有几个点可以一步到此点,入度就是几。

【回路】从此点出发能回到此点便是回路。


注意以下都是归属于图的

1.【树】

树嘛……以前觉得好深奥,其实,简单点,一个有向无回路图就是树,如上(哇塞,真的好像一棵树诶!!)。

【二叉树(BT)】咳咳……二叉树,理论最多的东西,让我们继续愉(xiang)快(gu)地不聊了吧。

好像还没说二叉树是啥,反正就是每个点的下面的结点(称之为儿子)最多只有2个的树。

好的,关于二叉树有两个东西:完全二叉树和满二叉树。

【满二叉树】除最后一层的结点外,其他的都有两个儿子的二叉树,说起来好吓人,其实很简单。

【完全二叉树】连续的二叉树(好像这解释很智障,但我真的不知道怎么解释了,图中自有真相):


树的题嘛……

可以点这里<–


 

【堆】这个看这里<–

 

【最短路径】:最简(bao)单(li)易(chao)懂(shi)的代码:Floyed,稍(bu)微(yi)难(chao)一(shi)点的:Dijkstra,请参看:
【图】最短路径——Floyed算法和Dijkstra算法

如果你想更快,那就上天吧……

好吧,当然有更快的FordSPFA,这东西最适合装逼了,当然装逼是有代价的——复杂——想知道自己点击前面。

 

【并查集】,请看这里<–

 

图最容易考到的都差不多了,呼……

#——————————————————————————–#

另外的数据结构:

【栈】最形象的比喻:一个杯子,只能从上面进,上面出。什么意思?就是先进去的后出,后进去的先出,如图。


假如有一列数:1,2,3,4,让我们来模拟一下栈:

初始:栈为空。

①1入栈。栈中的数为(从下往上):1;

2入栈。栈中的数为(从下往上):1,2;

③3入栈。栈中的数为(从下往上):1,2,3;

④4入栈。栈中的数为(从下往上):1,2,3,4;

⑤出栈(只有一种操作,因为你只能取出栈顶元素,不能取出中间的元素):4出栈,栈中的数为(从下往上):1,2,3;

⑥出栈(只有一种操作,因为你只能取出栈顶元素,不能取出中间的元素):3出栈,栈中的数为(从下往上):1,2;

⑦出栈(只有一种操作,因为你只能取出栈顶元素,不能取出中间的元素):2出栈,栈中的数为(从下往上):1;

出栈(只有一种操作,因为你只能取出栈顶元素,不能取出中间的元素):1出栈,栈为空。

就这样了,栈有什么用呢?首先最直观的:逆序输出一列数,当然你可以用数组,但是……一会再讲。

事实上,递归就是栈的使用,只不过是一层存了很多条代码,而不是只存一个数。

装逼程序(逆序输出):

 

  1. <span style=”font-family:SimHei;”>#include<cstdio>
  2. bool f=1;
  3. void go(int x)
  4. {
  5.     if(scanf(“%d”,&x)==1) go(x);
  6.     if(f) {f=0; return;}//因为用无个数的输入,所以要处理一下
  7.     printf(“%d ”,x);
  8. }
  9. int main()
  10. {
  11.     int a;
  12.     go(a);
  13.     return 0;
  14. }
  15. </span>

注意,这样的无个数输入需要最后Ctrl+Z一下,代表输入结束,如果你用了freopen或者是机测就不用在意,反正这样写交NOIP不会错~

 

然后要说的是,栈会溢出的!如果输入有超过2万的数,基本上是不行了,交上去会……运行错误。

好的,栈也是有专门的容器的,你也可以不用递归:

头文件:stack
声明方式:stack <存储类型> 栈的名字;
如:stack <int> a;
入栈:栈的名字.push(入栈的数);
如:a.push(x); a.push(2);
返回栈顶元素的值(不删除此元素):栈的名字.top();
如:x=a.top();
删除栈顶元素(不返回此元素的值):栈的名字.pop();
如:a.pop();
判断栈是否为空:栈的名字.empty(); 空返回true,反之返回false。
如:if(!a.empty()) return 0;//如果栈为空便结束
返回栈中元素个数:栈的名字.size();
如:len=a.size();

关于栈就这些了,接下来还有队列【吐血】……

 

好的,接下来是队列了

【队列】最形象的比喻:一个管子,只能从后面进,从前面出。什么意思?就是先进去的先出,后进去的后出,如图。


假如有一列数:1,2,3,4,让我们来模拟一下队列:

①1入队。队列中的数为(从左往右):1;

2入队。队列中的数为(从左往右):1,2;

③3入队。队列中的数为(从左往右):1,2,3;

1入队。队列中的数为(从左往右):1,2,3,4;

⑤出队(只有一种操作,因为你只能取出队头元素,不能取出中间的元素):1出队,队列中的数为(从左往右):2,3,4;

⑥出队(只有一种操作,因为你只能取出队头元素,不能取出中间的元素):2出队,队列中的数为(从左往右):3,4;

⑦出队(只有一种操作,因为你只能取出队头元素,不能取出中间的元素):3出队,队列中的数为(从左往右):4;

⑧出队(只有一种操作,因为你只能取出队头元素,不能取出中间的元素):4出队,队列为空。

队列又有什么用呢?首先肯定是广搜啦,然后队列里还有一种极其恶心的优先队列……不想说什么了,其实就是堆而已。

至于例子嘛,自己看广搜(我不会告诉你我是用数组模拟队列的)

如果要用标准队列……如下:

头文件:queue
声明方式:queue <存储类型> 队列的名字;
如:queue <int> a;
入栈:队列的名字.push(入栈的数);
如:a.push(x); a.push(2);
返回队头元素的值(不删除此元素):队列的名字.frond();
如:x=a.frond();
删除队头元素(不返回此元素的值):队列的名字.pop();
如:a.pop();
判断队列是否为空:队列的名字.empty(); 空返回true,反之返回false。
如:if(!a.empty()) return 0;//如果队列为空便结束
返回栈中元素个数:栈的名字.size();
如:len=a.size();

 

如果你以为完了,呵呵,还有,链表!

#——————————————————————————–#

好吧先说到这,有时间再写                                                                                                                                                             By WZY

 

 

⑤出栈(只有一种操作,因为你只能取出栈顶元素,不能取出中间的元素):4出栈,栈中的数为(从下往上):1,2,3;

⑥出栈(只有一种操作,因为你只能取出栈顶元素,不能取出中间的元素):3出栈,栈中的数为(从下往上):1,2;

⑦出栈(只有一种操作,因为你只能取出栈顶元素,不能取出中间的元素):2出栈,栈中的数为(从下往上):1;

⑧出栈(只有一种操作,因为你只能取出栈顶元素,不能取出中间的元素):1出栈,栈为空。