刘汝佳-数据结构入门
数论(二)
• 需要的次数满足[Lamé]:
• 即steps<=4.785lgN + 1.6723 • Lamé定理: 算法的最坏情况为计算gcd(Fn, Fn-1) • Heilbronn定理: 平均次数12ln2/π2lgn=0.843lgn
Euclid算法
• 递归形式和迭代形式(效率基本相当)
int gcd(int a, int b){ return (b? gcd(b, a%b) : a); }
int gcd(int a, int b){ int t; while(b){ t = a % b; a = b; b = t; } return a; }
分析
• 如何通过这组解求出所有解? • 如果有两组解(x1, y1)和(x2, y2), 有
优化
• 枚举过程也可以优化一下
– 优化一:除了2以外偶数都不是素数,所以每 次i可以增加2
– 优化二:除了2、3以外,素数p除以6的余数只 能是1或5,所以可以修改为每次交替增加2, 4
• 时间优化并不明显, 但空间分别缩小为原来 的1/2和1/3
分析
• 时间复杂度显然为筛的时间复杂度+O(n) • 筛的过程不超过n/2+n/3+n/5+… • 由公式
if(p%i==0) return false; return true;
优化
• 也可以改成只用素数试除, 速度变快但速度 并不是很明显(n=107时约两倍).
• 其中primes初始化为2和3, 随着循环测试的 进行不断更新
for(i=0; primes[i]*primes[i]<= p; i++) if(p % primes[i] == 0) return false;
动态规划(二) 刘汝佳 黑书 课件 经典
定理
定理 定理(F.Yao): 若w满足四边形不等式, 则d也 满足四边形形不等式, 即 d[i,j]+d[i',j']<=d[i',j]+d[i,j'], i<=i'<=j<=j' 证明: 对长度i=j'-i归纳, 显然l<=1时正确. i=i' 或j=j'时(同一行或同一列), 等式显然成立
– 情形 i'=j, 退化为反三角不等式 情形1: – 情形 i'<j 情形2:
情形1. 反三角不等式
i'=j时, d[i,j]+d[i',j']<=d[i',j]+d[i,j']退化为 d[i,j]+d[j,j']<=d[i,j'] 设k为让d[i,j']取最小值的决策(有多个时取最 大的一个k, 后同). 若k<=j, 则k是计算d[i,j]考虑过的合法决策 合法决策 d[i,j]<=w[i,j]+d[i,k-1]+d[k,j] 两边加上d[j,j'], 得 d[i,j]+d[j,j']<=w[i,j]+d[i,k-1]+d[k,j]+d[j,j']
决策单调性
进一步地, d的凸性可以推出决策的单调性 设k[i,j]为让d[I,j]取最小值的决策, 下面证明 k[i,j]<=k[i,j+1]<=k[i+1,j+1], i<=j 即: k在同行同列上都是递增的 证明: i=j时显然成立. 由对称性, 只需证明 k[i,j]<=k[i,j+1]. 记dk[I,j]=d[I,k-1]+d[k,j]+w[I,j], 则只需要证明对于所有的i<k<=k'<=j, 有 dk'[i,j]<=dk[i,j] dk'[i,j+1]<=dk[i,j+1]
数据结构(算法艺术)
二进制计数器问题
定义当前计数器的势能为它包含的1的个数, 则Φ0=0,Φi≥0(i>0)均成立,势函数合法 令第i个操作把a个0变成1,把b个1变成0,则ci = a+b,势能增量 为a-b,因此αi = 2a = 2
并查集的时间复杂度
结论(非最优)
m次操作,其中有n个是MAKESET操作 运行总时间为O(mlog*n) log*n定义为最小的i使得log(i)n≤1 log(i)n即n连续取i次以2为底的对数
结论三:共有log*n个块,每块b的元素最多有n/(2^^b)个
并查集的会计分析
MAKESET和UNION每次都只用常数时间 只需证明m个FIND所花时间是O(mlog*n) 分析FIND(x0)
从x0开始经过序列x0,…,xL到达根结点xL。 让这个序列中的每个元素投资1美元 但是不同的元素可能对不同的项目进行投资 序列中rank是严格递增的 各rank属于log*n个块,每个块最多n/(2^^b)个
可扩充性:并查集、线段树 实现复杂度:AVL树 vs Treap
功能数据结构举例
并查集 堆 哈希表 排序二叉树 线段树
并查集:概念与操作
不相交集合
合并 查找
Tarjan数据结构(Tarjan 1975): 森林表示法
每棵树一个集合, 根为集合标识 树的形态并不重要, 重要的是有哪些节点
分块
某个元素i的rank为rank(i),那么它被分到第log*(rank(i))块。 rank最大值为[log2n],因此最大块的编号为log*(log2n) = log*n–1。 可以证明,每一个块b里面元素最多有n/(2^^b)个
第r块元素不超过n/2r个 对r从2^^(b-1)+1到2^^b对n/2r求和
刘汝佳图结构和基本问题
边数E不超过V(V-1)/2
图形表示
此二图是同一个图的 不同表示. 图中并不定 义各个结点的位置以 及边的形态(直线?曲 线?), 关键是有哪些 点,哪些点相连 所有边如下表
其他术语
Edge包含u, v两个成员 AdjList支持取beg, nxt和end测试
图IO的ADT
当图需要进行IO的时候,通常需要支持以 下操作
– scanEZ: 读入边列表(顶点用0,1,…V-1表示) – scan: 读入边列表(顶点用符号表示) – show: 输出边列表
基本图问题
和图有关的问题通常有三种
有向图
边都是单向(unidirectional)的, 因此边(u,v)是有序数对. 有 时用弧(arc)专指有向边 在有向边(u, v)中, u和v分别叫
– 源(source)和目的(destination) – 尾(tail)和头(head), 不过和数据结构有冲突
有向无环图(directed acyclic graph, DAG)不是树, 它的基 图(underlying undirected graph)也不一定是树
上面两个图同构, 但不和下面的图同构
路径和圈
一条路径(path)是一个结点序列, 路上的相邻结点 在图上是邻接的. 如果结点和边都不重复出现, 则称为简单路径 (simple path). 如果除了起点和终点相同外没有重 复顶点和边, 称为圈(cycle). 不相交路(disjoint path)表示没有除了起点和终点 没有公共点的路. 更严格地
带权图
可以给边加权(weight), 成为带权图, 或加权图 (weighted graph). 权通常代表费用,距离等, 可 以是正数, 也可以是负数 也可以给点加权, 或者边上加多种权 带权有向图一般也称为网络(network) 带权图的问题多为组合优化问题, 在运筹学中有 广泛应用
1.1-编程的灵魂——数据结构+算法=程序
2021/4/6
2
使用说明
• ★符号表示重点要求 • △符号表示只需要了解,不用深入学习
2021/4/6
3
第1章 算法与数据结构
• 1.1 编程的灵魂——数据结构+算法=程序 • 1.2 基本算法 • 1.3 数据结构(1)——入门 • 1.4 数据结构(2)——拓宽与应用举例 • 1.5 动态规划 • 1.6 状态空间搜索
• 步骤二: 设计测试数据. 一般来说可以使用 实际数据、随机数据和特殊(甚至错误)数据. 随机数据保证分析结果是针对算法而不是 数据的, 而特殊数据确保算法能正确处理一 切给它的输入
2021/4/6
14
其他
• 常见错误
– 不注意程序效率. 高效算法往往更复杂, 但是一 但写成, 能节约大量的执行时间
• 需要重新实现基本算法的情况
– 更好的理解算法, 为特定应用微调算法 – 新计算环境, 无库可用, 且硬件特性不一致
• 实现的优化程度: 本文一般只考虑不过分牺 牲算法效率下相对自然简洁的实现方式
• 真实的实现: 过度优化、错误处理、维护性
2021/4/6
12
实验比较
• 侧重点
– 算法分析: 两个算法同阶, 只相差一个常数 – 实验比较: 一个运行3秒, 一个运行30秒
• 算法分析和实验比较
– 实验比较可以验证算法分析的结果. 是否真的 只相差一个常数?
– 算法分析可以帮助估算实际运行时间. 愿意等 待10个小时?
2021/4/6
13
实验比较的步骤
• 步骤一: 实现算法. 简单的算法并不困难, 但 是复杂的算法的实现具有挑战性, 若想得到 高效的实现需要关注一些算法细节而不光 是编码细节
刘汝佳基础数据结构
例3. 移动窗口
有一个长度为n 的数组,还有一个长度为 k<=n 的窗口.窗口一开始在最左边的位 置,能看到元素1, 2, 3, …k.每次把窗口往 右移动一个元素的位置,直到窗口的右边 界到达数组的元素n.
分析
考虑最小值.假设窗口从左往右移动
… 2 3 5 4 …
保存5是不明智的, 因为从现在开始一直到5 离开窗口, 5始终被4"压制",永远都不可能 成为最小值.删除5不会影响结果 启发:算最小值的有用元素形成一个递增 序列,最小值就是队首元素 关键:窗口右移操作
向下调整
首先选取当前结点p的较大儿子. 如果比p大, 调整 停止, 否则交换p和儿子, 继续调整
13 13 2 2 4 4 8 8 9 10 9 10 5 5 11 12 11 12 6 6 3 3 7 7 8 8 4 4 9 10 9 10 13 13 5 5 11 12 11 12 6 6 2 2 3 3 7 7
堆的建立
从下往上逐层向下调整. 所有的叶子无需调整, 因此从hs/2开始. 可用数学归纳法证明循环变量 为i时, 第i+1, i+2, …n均为最小堆的根
void insert(int a) { heap[++hs]=a; swim(hs); } int getmin() { int r=heap[1]; heap[1]=heap=[hs--]; sink(1); return r; } int decreaseKey(int p, int a) { heap[p]=a; swim(p); } void build() { for(int i=hs/2;i>0;i--) sink(i); }
注意:OP 语句的参数不能为变量n,而LOOP 语 句的参数可以为变量n,但不允许有其他名字的变 量.这样,整个程序的时间复杂度应为n 的多项 式.你的任务就是计算并显示出这个多项式.
String 算法竞赛入门经典 刘汝佳概要
Linear Cellular AutomataA biologist is experimenting with DNA modification of bacterial colonies being grown in a linear array of culture dishes. By changing the DNA, he is able ``program" the bacteria to respond to the population density of the neighboring dishes. Population is measured on a four point scale (from 0 to 3. The DNA information is represented as an array DNA, indexed from 0 to 9, of population density values and is interpreted as follows:In any given culture dish, let K be the sum of that culture dish's density and the densities of the dish immediately to the left and the dish immediately to the right. Then, by the next day, that dish will have a population density of DNA[K].The dish at the far left of the line is considered to have a left neighbor with population density 0.The dish at the far right of the line is considered to have a right neighbor with population density 0. Now, clearly, some DNA programs cause all the bacteria to die off (e.g., [0,0,0,0,0,0,0,0,0,0]. Others result in immediate population explosions (e.g.,[3,3,3,3,3,3,3,3,3,3]. The biologist is interested in how some of the less obvious intermediate DNA programs might behave.Write a program to simulate the culture growth in a line of 40 dishes, assuming that dish 20 starts with a population density of 1 and all other dishes start with a population density of 0.InputThe input begins with a single positive integer on a line by itself indicating the number of the cases following, each of them as described below. This line is followed by a blank line, and there is also a blank line between two consecutive inputs.For each input set your program will read in the DNA program (10 integer values on one line.OutputFor each test case, the output must follow the description below. The outputs of two consecutive cases will be separated by a blank line.For each input set it should print the densities of the 40 dishes for each of the next 50 days. Each day's printout should occupy one line of 40 characters. Each dish is represented by a single character on that line. Zero population densities are to be printed as the character ` '. Population density 1 will be printed as the character `.'. Population density 2 will be printed as the character `x'. Population density 3 will be printed as the character `W'.Sample Input10 1 2 0 1 3 3 2 3 0Sample Outputbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb...bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.xbx.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bb.bb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.........bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.xbbbbbbbx.bbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbxbbbbbxbb.bbbbbbbbbbbbbbbbbbbbbbbbbb...xxxbbbxxx...bbbbbbbbbbbbbbbbbbbbbbbb.xb.WW.xbx.WW.bx.bbbbbbbbbbbbbbbbbbbbbb.bbb.xxWb.bWxx.bbb.bbbbbbbbbbbNote: Whe show only the first ten lines of output (the total number of lines must be 50 and the spaces have been replaced with the character "b" for ease of reading. The actual output file will use the ASCII-space character, not "b".。
蓝书刘汝佳算法竞赛入门经典勘误
#《算法竞赛入门经典》勘误关于勘误?下面的勘误很多来自于热心读者,再次向他们表示衷心的感谢!我并不清楚这些错误实际是在哪个版本中改正过来的,所以麻烦大家都看一下。
有发现新错误的欢迎大家在留言中指出,谢谢!一些一般性的问题?运算符<?、>?已经被废弃,请用min、max代替(代码仓库中的代码已更新,g++ 4.6.2下编译通过)重大错误?p24. 最后一行,“然后让max=INF,而min=-INF”应该是“然后让max=-INF, 而min=INF”。
(感谢imxivid)p43. 最后,判断s[i..j]是否为回文串的方法也不难写出:int ok = 1; for(k = i; i<=j;i++)应该为for(k = i; k<=j; k++)(感谢imxivid)p45. 第七行和第九行i-j+1应为i+j+1。
修改后:1. {2. for (j = 0; i - j >= 0 && i + j < m; j++)3. {4. if (s[i - j] != s[i + j]) break;5. if(j*2+1 > max) { max = j*2+1; x = p[i - j]; y = p[i + j];} 6. } 7.for (j = 0; i - j >= 0 && i + j + 1 < m; j++) 8. { 9. if(s[i - j] != s[i + j + 1]) break; 10. if (j*2+2 > max) 11. {max = j*2+2; x = p[i - j]; y = p[i + j + 1]; } 12. }13. }p53. 例题4-1. 组合数. 输入非负整数n和m,这里的n和m写反了。
应是“输入非负整数m和n”。
p54. 举例中的m和n也写反了(真是个悲剧),且C(20,1)=20。
刘汝佳 算法竞赛入门经典授课教案第2章_循环结构程序设计
刘汝佳第2章循环结构程序设计【教学内容相关章节】2.1for循环 2.2循环结构程序设计 2.3文件操作2.4小结与习题【教学目标】(1)掌握for循环的使用方法;(2)掌握while循环的使用方法;(3)学会使用计算器和累加器;(4)学会用输出中间结果的方法调试;(5)学会用计时函数测试程序效率;(6)学会用重定向的方式读写文件;(7)学会fopen的方式读写文件;(8)了解算法竞赛对文件读写方式和命名的严格性;(9)记住变量在赋值之前的值是不确定的;(10)学会使用条件编译指示构建本地运行环境。
【教学要求】掌握for循环的使用方法;掌握while循环的使用方法;掌握几个常用的文件操作库函数fopen、fclose、fprintf、fscanf等。
【教学内容提要】在有些程序中,需要反复执行某些语句。
将n条相同的语句简单地复制会使程序变得不合理的冗长,因此高级语言中提供了支持程序重复执行某一段程序的循环控制语句。
相关的语句有:for、while、do while、break、continue等。
既可以从文件中读取数据, 也可以向文件中写入数据。
读写文件之前,首先要打开文件。
读写文件结束后,要关闭文件。
C/C++提供了一系列库函数,声明于stdio.h中,用于进行文件操作。
这里介绍其中几个常用的文件操作库函数fopen、fclose、fprintf、fscanf等。
【教学重点、难点】教学重点:(1)掌握for循环的使用方法;(2)掌握while循环的使用方法;(3)掌握文件有关操作;(4)条件编译。
教学难点:(1)掌握for循环的使用方法;(2)掌握while循环的使用方法;【课时安排(共2学时)】2.1for循环 2.2循环结构程序设计 2.3文件操作2.4小结与习题2.1 for循环请用for循环实现输入正整数n,打印1,2,3,…,10,每个占用一行。
程序如下:程序2-1 输出1,2,3,…,n的值#include <stdio.h>int main(){int i, n;scanf("%d", &n);for(i = 1; i <= n; i++)printf("%d\n", i);return 0;}提示2-1:for循环的格式:for(初始化;条件;调整) 循环体;提示2-2:尽管for循环反复执行相同的语句,但这些语句每次的执行效果往往不同。
游戏开发技术掠影刘汝佳算法与数据结构课程网站课件
游戏图形技术是游戏开发中最为重要的技术之一,它决定了游戏的视觉表现和用户体验。游戏图形技术包括二维图形、三维图形、光照与材质等方面的技术。
数据结构分类
根据实际需求选择合适的数据结构,以提高程序的效率和可维护性。
数据结构选择
数据结构
游戏开发中常用的数据结构有数组、链表、栈、队列、哈希表等。这些数据结构在游戏逻辑处理、游戏对象管理等方面发挥着重要作用。
排序算法
在游戏开发中,排序算法常用于处理游戏对象的管理和渲染顺序。常见的排序算法有冒泡排序、选择排序、插入排序等。
Lua、C#、JavaScript等,用于实现游戏逻辑和交互功能。
美术资源
Photoshop、3D建模软件(如Blender、Maya)等,用于制作游戏中的图像和模型。
音效软件
Audacity、Adobe Audition等,用于制作和编辑游戏音效。
游戏开发案例分析
05
总结词:游戏AI算法是游戏开发中的重要组成部分,它能够让游戏中的角色具备智能行为,提高游戏的可玩性和挑战性。详细描述:游戏AI算法的实现需要考虑角色的行为决策、行动规划、学习能力等多个方面。例如,在角色行为决策方面,可以采用基于规则的方法或基于概率的方法,根据游戏设定的规则和环境信息,让角色能够做出合理的决策。在行动规划方面,可以采用路径搜索算法或行为树算法,根据角色的目标和环境信息,规划出最优的行动路径或行为序列。在学习能力方面,可以采用强化学习算法或深度学习算法,让角色能够在游戏中不断学习和进化,提高游戏难度和挑战性。
算法竞赛-入门经典-作者刘汝佳
算法竞赛-入门经典-作者刘汝佳.doc第1部分语言篇第1章程序设计入门学习目标☑熟悉C语言程序的编译和运行☑学会编程计算并输出常见的算术表达式的结果☑掌握整数和浮点数的含义和输出方法☑掌握数学函数的使用方法☑初步了解变量的含义☑掌握整数和浮点数变量的声明方法☑掌握整数和浮点数变量的读入方法☑掌握变量交换的三变量法☑理解算法竞赛中的程序三步曲:输入、计算、输出☑记住算法竞赛的目标及其对程序的要求计算机速度快,很适合做计算和逻辑判断工作。
本章首先介绍顺序结构程序设计,其基本思路是:把需要计算机完成的工作分成若干个步骤,然后依次让计算机执行。
注意这里的“依次”二字——步骤之间是有先后顺序的。
这部分的重点在于计算。
接下来介绍分支结构程序设计,用到了逻辑判断,根据不同情况执行不同语句。
本章内容不复杂,但是不容忽视。
注意:编程不是看会的,也不是听会的,而是练会的,所以应尽量在计算机旁阅读本书,以便把书中的程序输入到计算机中进行调试,顺便再做做上机练习。
千万不要图快——如果没有足够的时间用来实践,那么学得快,忘得也快。
(为帮助没有分值的朋友能下载,特此修改文档,以免上传不了)1.1 算术表达式计算机的“本职”工作是计算,因此下面先从算术运算入手,看看如何用计算机进行复杂的计算。
程序1-1 计算并输出1+2的值#include<stdio.h>int main(){printf("%d\n", 1+2);return 0;}算法竞赛入门经典这是一段简单的程序,用于计算1+2的值,并把结果输出到屏幕。
如果你不知道如何编译并运行这段程序,可阅读附录或向指导教师求助。
即使你不明白上述程序除了“1+2”之外的其他内容,仍然可以进行以下探索:试着把“1+2”改成其他东西,而不要去修改那些并不明白的代码——它们看上去工作情况良好。
下面让我们做4个实验:实验1:修改程序1-1,输出3-4的结果实验2:修改程序1-1,输出5×6的结果实验3:修改程序1-1,输出8÷4的结果实验4:修改程序1-1,输出8÷5的结果直接把“1+2”替换成“3+4”即可顺利解决实验1,但读者很快就会发现:无法在键盘上找到乘号和除号。
acm程序设计竞赛 数学基础 刘汝佳
数学基础(版本2009)刘汝佳例1. 同构计数•一个竞赛图是这样的有向图–任两个不同的点u、v之间有且只有一条边–不存在自环•用P表示对竞赛图顶点的一个置换。
当任两个不同顶点u、v间直接相连的边的方向与顶点P(u)、P(v)间的一样时,称该图在置换P下同构•对给定的置换P,我们想知道对多少种竞赛图在置换P下同构分析•先把置换它分解成为循环, 首先证明长度为L的偶循环将导致无解–对于点i1, 记P(i k)=i k+1, 假设i1和i L/2+1的边方向为i1->i L/2+1, 那么有i2->i L/2+2, i3->i L/2+3, …, i L/2+1->i1, 和假设矛盾!•假设确定其中k条独立边后其他边也会确定, 则答案为2k•考虑两类边: 循环内边和循环间边.分析•循环内顶点的关系–定了i 1和i j 之间的关系, i k 与i (k+j-2) mod n+1之间的关系也被确定下来了, 因此只需要确定i 1和i 2, i 3, …, i (L-1)/2+1这(L-1)/2对顶点的关系•不同循环间顶点的关系–设循环为(i 1,i 2,…,i L1)和(j 1,j 2,…,j L2), 通过类似分析得只需要确定gcd(L1, L2)对关系即可分析•最后答案为2k1+k2•其中k1=sum{(L-1)/2}, k2=sum{gcd(L1, L2)}•可以用二分法加速求幂例2. 图形变换•平面上有n个点需要依次进行m个变换处理•规则有4种, 分别把(x0,y)变为–平移M(x, y): (x0+x, y0+y)–缩放Z(L): (Lx0, Ly0)–翻转F(0): (x0, -y0); F(1): (-x0, y0)–旋转R(a): a为正时逆时针, 离原点距离不变, 旋转a度•给n(<=106)个点和m(<=106)条指令•求所有指令完成后每个点的坐标分析•如果直接模拟, 每次需要O(n)的时间, 一共O(nm), 无法承受•把点(x0, y)写成列向量[x, y]T, 则后3种变换可以都可以写成矩阵–缩放P’= Z * P, Z = [L 0; 0 L]–翻转P’= F * P, F = [1 0; 0 -1]或[-1 0; 0 1]–旋转P’= R * P, R = [cosa–sina; sina cosa]•可是无法实现平移, 怎么办呢?分析•修改表达方式, 令P = [x 0, y 0, 1]T , 则四种变换的矩阵M, Z, F, R 分别为100001,00001001x L M y Z L ⎡⎤⎡⎤⎢⎥⎢⎥==⎢⎥⎢⎥⎢⎥⎢⎥⎣⎦⎣⎦100100010010001001F −⎡⎤⎡⎤⎢⎥⎢⎥=−⎢⎥⎢⎥⎢⎥⎢⎥⎣⎦⎣⎦或cos sin 0,sin cos 0001a a R a a −⎡⎤⎢⎥=⎢⎥⎢⎥⎣⎦分析•只需要先计算所有变换矩阵的乘积A, 然后对于每个点, P’= A * P•注意: 矩阵乘法不满足交换律, 因此需要按照输入顺序进行乘法•每次矩阵乘法需要33=27次乘法, 则计算A一共需要27m次乘法, 对所有点变换需要27n 次乘法, 一共27(n+m)次例3. 染色方案•N*M(N<=10100, M<=5)的格子纸,每个格子被填上黑色或者白色。
动态规划(一) 刘汝佳 黑书 课件 经典
直接书写的程序
递归树分析
关键点二: 重叠子问题
• 为了让动态规划确实发挥功效, 问题应该包含尽 量多的重叠子问题(overlapping subproblems)
解决方法: 记忆化
• 注意memoization不是memorization
分析
• 设d[i,j]为以j项结尾的i段和的最大值, 则需要 枚举此段开头y和上一段结尾x, 即 d[i,j]=max{d[i-1,x] + a[y..j]} • 每次需要枚举x<y<=j, 决策量为O(n2), 状态 为O(nm), 共O(n3m) • 注意到如果a[j-1]也是本段的, 答案变成为 d[i,j-1]+a[j], 因此方程优化为 d[i,j]=max{d[i,j-1]+a[j], d[i-1,x]+a[j]}, x<j
自底向上递推
空间优化
• 如果只需要最优值, 可以用滚动数组实现 • 按照i递增的顺序计算, d[i,j]只和d[i-1,j]和 d[i,j-1]以及d[i-1,j-1]有关系,因此只需要保 留相邻两行, 空间复杂度为O(min{m,n}) • 更进一步的, 可以只保留一行, 每次用单独 的变量x保留d[i-1,j], 则递推方程为 If(i==j) d[j]=x; else { x = d[j]; d[j]=max{d[j-1], d[j]} };
分析
• 优化后状态仍然是二维的,但决策减少为 O(n), 总O(n2m) • 可以继续优化. 注意到时间主要耗费在对x 的枚举上, 计算max{d[i-1,x]}. 这个值… • 我们把d的第一维称为”阶段”, 则本题是 典型的多阶段决策问题
编程基础_刘汝佳
语法高亮:很多编辑器都有此功能 代码补全:完全不用。学会查资料 编译和运行:用插件、执行外部脚本 跟踪调试:完全不用。学会断言和打印调试信息
给NOI选手的建议
使用IDE,并记住一些常用快捷键,但不依赖其跟 踪调试功能
学一点简单的linux命令(NOI目前使用Ubuntu作为 操作系统), 若能会一点bash脚本的编写则更好
函数库更加强大:更方便的字符串函数、正规表达 式函数、高精度整数和实数、基本二维几何运算、 常用数论函数,以及很多竞赛里用不上但在其他领 域非常有用的函数
可以在ACM/ICPC和TopCoder中使用
如何学习一门语言
浏览文档。成熟语言的文档都已成熟 独立编写自己的程序,阅读他人的程序
多思考 小程序:熟练度和准确性优先 大程序:合理的设计、编码、调试与测试 程序阅读:风格和技巧
具体的迭代方法
“一次只写一点点”,确保它正确后再继续写 问题:
写多少之后需要停下来?模块化 如何确保它写对了?测试
写程序的顺序:自顶向下 vs 自底向上 模块化关键:兼顾低耦合与高内聚 测试的关键:单元测试、自动测试
提问时间
次常用的 最怕的 回忆… 最常用的 最直白的 最喜欢的 最早学的
关于Pascal
多数学生学会的第一门语言 方便教学,方便自学 陷阱比C/C++少,但比Java多 竞赛之外,应用范围很受限 竞赛之中,仍是主流语言,但高水平选手使用
Pascal的比例逐渐减少 由于应用范围太窄,Pascal不能在ACM/ICPC、
TopCoder和一些商业比赛中使用
关于C/C++
应用范围更广, 以后可以直接做事 很多陷阱!非法内存访问、递归栈溢出时不会
【免费下载】算法竞赛 入门经典 作者刘汝佳
算法竞赛入门经典
目录
目录................................................................................................................................................110 第 1 章 程序设计入门................................................................................................................112
1.1 算术表达式...................................................................................................................112 1.2 变量及其输入...............................................................................................................114 1.3 顺序结构程序设计.......................................................................................................117 1.4 分支结构程序设计.......................................................................................................120 1.5 小结与习题...................................................................................................................123
刘汝佳《算法入门经典》答案
1.double 类型的输出格式为“%lf”.2.double pi=4.0*atan(1.0);这样比较精确。
3.printf("d",m);可以在输出前补充上0,但是换做其他的字符就不可以了。
4.绝对值的输出用%d,如:printf("%d",abs(-10));不可以用%f.第二章循环结构程序设计(2011-07-21 21:40:01)[编辑][删除]分类:算法竞赛入门经典标签:杂谈1. 调试程序的新方法:IDE,gdb .(还没有学会)2.判断是否是整数 floor(m+0.5)==m.1.AABB问题:法一:列举,然后判断是否是完全平方数,m=1100*a+11*b; m==floor(m+0.5) 法二:枚举所有的数,然后分离位数,分别判断是否相等。
2.阶乘之和问题:输出结果的后六位printf("%d",s%100000)3.使用计时器,可以测试程序的效率#include<time.h>printf("%.2lf",(double)clock()/CLOCKS_PER_SEC);注意的是,将其放入程序时,测试时会把键盘输入的时间一块算上,而如果不计可以使用命令行测试。
4.文件操作:(1)输入输出重定向:#define LOCALint main(){#ifdef LOCALfreopen("data.in","r",stdin);freopen("data.out","w",stdout);#endif.........}(2)文件的保存和读取:int main(){FILE *fin,*fout;fin=fopen("data.in","rb");fout=fopen("data.out","wb");.....fscanf(fin,"%d",&a);fprintf(fout,"%d\n",a);fclose(fin);fclose(fout);}第三章:数组和字符串(2011-07-22 20:27:54)[编辑][删除]标签:分类:算法竞赛入门经典杂谈1.当数组需要开的很大的时候,数组a[1000]的定义放到main函数外,否则会异常退出。
高精度整数_刘汝佳
一些注释
右往左存储,每一位数字用一个字节表示,方 便打印,而且能简化后面的乘除运算。 符号位signbit为1(正)或者-1(负)。 lastdigit为最高位所对应的下标
void print_bignum(bignum *n) { if (n->signbit == MINUS) printf("-"); for(int i = n->lastdigit; i >= 0; i--) printf("%c", ’0’ + n->digits[i]); printf("\n"); }
绝对值加法
首先把结果的所有数字初始化为0,则最高位 的进位无需特殊处理 最后调用zero_justify来调整最高位下标 lastdigit的值。同时也把-0更正为0
c->lastdigit = max(a->lastdigit,b->lastdigit)+1; carry = 0; for(int i = 0; i <= (c->lastdigit); i++) { c->digits[i] = (char)(carry + a->digits[i] + b->digits[i])%10; carry = (carry + a->digits[i] + b->digits[i])/10; } zero_justify(c);
绝对值减法(保证差非负)
和加法类似,但需考虑借位
c->lastdigit = max(a->lastdigit,b->lastdigit); borrow = 0; for (int i = 0; i <= (c->lastdigit); i++) { v = (a->digits[i] - borrow - b->digits[i]); if (a->digits[i] > 0) borrow = 0; if (v < 0) { v = v + 10; borrow = 1; } c->digits[i] = (char) v % 10; } zero_justify(c);
ACM-ICPC比赛随想——刘汝佳
ACM-ICPC比赛随想——刘汝佳刘汝佳,1982年12月生,毕业于重庆外国语学校,清华大学计算机科学与技术系2005级研究生。
高二时创立“信息学初学者之家”网站(OIBH),高三入选IOI2001国家集训队。
大学一年级时获ACM/ICPC世界总决赛银牌(世界第四),IOI2002/03/04国家集训队指导老师。
曾与黄亮合作出版了《算法艺术与信息学竞赛》丛书,自2002年至今为科学委员会学生委员,在命题方面和辅导学生方面成绩突出,同时兼任NOI网站总监。
从第一次听说ACM/ICPC到现在,已经有快七个年头了。
最开始因好奇而关注,而现在因了解而关注——关注比赛,更关注参加比赛的人。
ACM/ICPC是一个五味瓶。
没有接触过它的人不会知道其中的酸甜苦辣,而一旦置身其中,每个选手都会对它产生一种特殊的感情,时间越长,这种感情也越复杂、越浓烈。
感情来源于对算法与题目的喜爱,来源于对成功的向往和失败的恐惧,来源于各种选择与放弃中的徘徊与摇摆不定,来源于程序世界与现实生活的巨大差异,也来源于通往理想的曲折道路——探索其中时的无助和艰辛。
等到退役的那一天,回过头来再看当时的自己,相信每位选手都会发现自己的很多方面成熟了许多——远不只是编程能力和算法功底。
以前我觉得这是比赛的副产品,而现在我认为这才是比赛的主要目的,至少对于选手自己是如此。
虽然我从心里喜欢这个比赛,但我并不鼓励每个人参加。
并不是每个人在每个时期都适合参加这个比赛,且适合的人选也并不代表一定能取得好成绩——比赛场上是没有“一定”的,任何一个选手都必须有勇气承担风险,就像所有其他有潜在回报的事一样。
另一方面,对于所有下定决心参加比赛的选手,我鼓励他坚持到底,因为只有这样才会受到真正的磨练。
在“参加”与“不参加”的岔路口上,大多数选手被两个问题所困绕。
第一个是“我能获奖么?奖会给我带来什么好处?”,第二个是“抛开荣誉,从比赛中学到的东西值得我花费这么多时间吗?”。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
• 经典例子:哈希表,二叉堆,并查集,线段树
链结构应该根据元素间关系(链接)进行“移动”
• 经典例子:伸展树,二项堆,跳跃表
*特殊算法需要自己归纳出ADT并设计逻辑结构
PQ树,后缀树
8
常见的抽象数据类型
栈
压栈(入栈),弹栈(出栈),判断空
队列
入队,出队,判断空
5,4,3,2,1
1,2,3,4,5
B
A
C 12
队列
外特性:先进先出(FIFO)
食堂排队 吸管里的饮料 作用:维持顺序 数组实现:元素queue[0..maxn-1],队首front,队尾rear
入队:inc(rear); queue[rear] := ele; 出队:ele := queue[front]; inc(front) 队空条件:front > rear 问题:出队的元素还在数组里,不是很浪费吗?
2
抽象数据类型(ADT)
算法对数据结构的要求
维护一个电话薄,方便进行插入删除和查找
操作:插入,删除,查找 如何实现? 算法不关心! 只要提供这些操作都可以! 抽象数据类型:集合
3
逻辑结构
抽象数据类型没有办法转化成程序
需要设计逻辑结构!
所有电话记录形成线性结构,记录的顺序无要求
newx := x + dx[direction]; newy := y + dy[direction];
八连?自己试一试! 空间呢? 六角形呢?
19
回到原题
如何找脸?
四连?八连?
如何找脸上的元素?
针top
入栈:inc(top); stack[top] := ele; inc(top) top := top + 1 出栈:ele := stack[top]; dec(top) dec(top) top := top - 1 空栈条件:top = 0
11
铁轨问题
例1:1,2,3,4,5 yes;例2:5,4,3,2,1 yes 例3:3,2,4,5,1 yes;例4:3,1,4,5,2 no
6
比较一下
方案
逻辑结构 物理结构 特点
无序数组 无序线性表 数组
差
无序链表
链表
插入快
有序数组 有序线性表 数组
查找快
有序链表
链表
比较平衡
7
如何学习数据结构
了解常见的抽象数据类型 对每种ADT,了解常见的逻辑结构
设计逻辑结构是最难的!
对给定的逻辑结构,自己设计物理结构
物理结构一般只是数组和链结构
• 无序线性表
有了逻辑结构,可以设计算法
• 插入:直接插到表的头部或者尾部 • 删除:直接删除,再把两段合并在一起 • 查找:从头开始沿着表找一遍
这个算法仍然不能转化成程序
• 甚至无法进行复杂度分析!
4
物理结构
逻辑结构需要进一步转化成物理结构
逻辑结构:无序线性表 物理结构:数组
插入:插到尾部比较方便,O(1) 删除:“合并两半”导致元素移动,最坏O(n) 查找:最坏O(n)
时间复杂度:O(n) (想一想,为什么不是O(n2))
16
种子填充法
(a)笑脸
(ห้องสมุดไป่ตู้)非笑脸
图 1-16 笑脸和非笑脸示例
17
怎样找连通区域?
走迷宫——没有分身术
栈(DFS)
撒墨水
队列(BFS)
连通区域的种类
四连 八连 怎样高效的实施?
18
四连区域和八连区域
四连
dx : array[1..4] of integer = {1, -1, 0, 0}; dy : array[1..4] of integer = {0, 0, -1, 1}; for direction :=1 to 4 do
物理结构:链表
插入:插到头部比较方便,O(1) 删除:找到位置后O(1) 查找:O(n)
不同的物理结构,得到不同的效果!
5
另一种逻辑结构
有序线性表
物理结构:数组
插入最坏O(n) 删除最坏O(n) 查找最坏O(logn):二分查找
物理结构:链表
插入(找到后)最坏O(1) 删除(找到后)最坏O(1) 查找最坏O(n),平均n/2
循环队列
把队列看成环行的,则 入队:rear := (rear + 1) mod maxn; 不定义为queue[1..maxn]的原因 出队:front := (front + 1) mod maxn; 可能存在队满的情况:条件也是front > rear (想一想,如何解决?)
13
小球钟——时间与运动
串
取前缀/后缀,匹配…
字典
插入,删除,查找
优先队列
插入,取最小值,删除最小值,合并
9
入门学什么?
*栈和队列 串:匹配 树:表示和遍历 图:遍历和拓扑排序 *基本检索算法 *基本排序算法
10
栈
外特性:后进先出(LIFO)
交卷子 KTV的“点歌单” 作用:保护现场 逻辑结构:只在一端操作的线性表 数组实现:元素stack : array[1..maxn] of integer,栈顶指
经过12小时
小球又全部回到底部轨道 但顺序和以前不一样了! 设一开始各个小球的编号依次为1,2,3…n 新顺序是1,2,3…n的一个排列! 一个例子:1,2,3,4,5 2,4,5,1,3
小球1的位置变化:1421 小球3的位置变化:353 小球1,2,3,4,5分别经过3,3,2,3,2个12小时回到原地 总时间为[2,3] = 6
数据结构入门
清华大学 刘汝佳
1
什么是数据结构
数据结构的含义
数据 关系 操作
例子:数组
数据:a[1], a[2], …, a[n] 关系:前驱/后继 操作:随机存取,插入,删除…
数据结构为算法服务
根据算法对数据的操作要求,设计合适的数据结构 实现同一套操作,可以用多种数据结构 如何降低时空复杂度,又方便实现?
14
球钟的状态表示
含有小球的四个部件
分钟轨道:ball[1..4] 5分钟轨道:ball[1..11] 小时轨道:ball[1..11] 底部轨道:ball[1..n]
问题:虽然都是线性结构,但如何操作?
分钟、5分钟、小时:栈! 底部小球:队列!
15
球钟的模拟
完全模拟
时间复杂度O(t),t为模拟的时间,可能很大