在线OJ题库部分解析
测试网站主要为书中ZOJ,HDOJ。如果你准备入门acm,那以下题目顺序循序渐进,很适合各个阶段新手刷题。
前言
本书介绍:ACM大学生程序设计竞赛在线题库最新精选题解
本书OJ入口:HDOJ、ZOJ
说实话我觉得新手确实需要一份刷题单,带解析那种书。我个人也是新手才开这本书的题,连续追更中……我觉得写完读别人代码有很大提升,但是看题解大多数oj并不支持阅读其他人ac代码,而且很难找到优秀的代码。这本书给的代码让我学会很多奇巧淫技 。
洛谷题单还行就是不太循序渐进而且刷的题太多。这本书题单大多不错而且基本难度递增。不过就是解析太少而且给的有些程序已经过时或者有些许bug,所以本文主要为了指出书中部分不当之处与精简部分更为优秀的题目(仅为个人意见)。
提示:本书为当前最新版(2019年7月),本文按章节给出部分值得一刷的题题解并指出书中代码些许不当之处。
对于给出“并不推荐”的题一般有以下原因:
①写该题并不能提高模拟能力;
②题目较怪;
③该题是思维题,难点不在编程并且无细节需注意。
第1章基础编程技巧题
本章基本水题,但是对新生来说必不可少,可以增强信心 ,可以把基本功打牢,比如sum总是忘初始化0,输出格式不对,二分边界总写错等等常见bug(虽然我也总wa在奇奇怪怪的点
题目列表:
HDOJ1056-HangOver:此题推出公式应该没什么问题,掌握浮点输出应该不会错。值的注意的是int/int还是int,所以以后有double时吧所有整型常数都变成xx.0形式。
并不推荐,可以参考下题练习:PATL3-013非常弹的球题解。
HDOJ1735-字数统计:此题出的有点烂,但是没刷过模拟题的可以试试,实际上有两种情况题解没有考虑到,应该是出题人的锅,可以改进一下算法,参见HDOJ1735-字符统计题解,不必要再用一遍for循环记录空白数,直接第一次读入时用index记录最后一行最后有字的位置即可。
ZJU1058-Currency Exchange:并不推荐。
ZJU1067-Color Me Less:并不推荐。
ZJU1078-Palindrom Numbers:强烈建议新手不看书写一遍,然后看书题解,再独立写一遍。,简单的回文判断和进制转换至少不能超过3分钟写完,简单题都要调试半天的话那比赛中难题会更卡。
ZJU1090-The Circumference of the Circle:不建议写,思维题。
ZJU1095-Humble Numbers:丑数,建议看书后能独立完成,毕竟赛场过不了的题打表是基本操作。
ZJU1105-FatMouse’s Tour:题意较怪,可以一试,不过书中代码有坑,大多数测评机c++已经不支持gets(),书中代码用C(gcc 6.5.0)编译可以AC。此外书中%dist应为印刷错误,应该是%d。参见ZJU1105-FatMouse’s Tour题解
ZJU1151-Word Reversal:简单题,新手建议写写。
ZJU1154-Niven Numbers:水题,建议新手看书后独立完成。
第2章模拟编程技巧题
本章算是正式入门第一步,比赛很少会出大型模拟题(毕竟只有几个小时),但是并不是说训练没用,新手写模拟题会极快增强读题能力,毕竟纯英文而且为了强行造题,题意经常很怪,谁还没比赛读错题过 。另一点是模拟题需要考虑的特殊情况较多,比赛一个特殊点过不了都算wa,写模拟可以训练补充漏洞以及造数据能力。可以学一下acm对拍数据。
HDOJ1035-Robot Motion:模拟题,新手可以一试。
HDOJ猜数字:暴力循环1000-9999即可。新手随便写写。
HDOJGrey Area:书中印错一处,图2-1应为图2-2。鉴于没有源码参考发篇题解:HDOJGrey Area题解
HDOJ-Scaring the brid:可以用深搜但没必要,数据很小,建议按书上做法枚举所有状态,最多210,尝试一下状态压缩做法。另外本题有一个坑点即judge函数的s从0开始,因为有测试数据是所有稻草人位置占了整个图,这样0个也可以过。
HDOJ-ArcSoft’s Office Rearrangement:思维题,但是值得试试。
ZJU1003-Crashing Balloon:因数分解,递归做法。建议独立写写。
ZJU1006-Do the Untwist:凯撒密码,很水,看心情写~
ZJU1016-Parencodings:建议独立完成,看完题解可以再试试。
ZJU1039-Number Game:本题出的很突兀,可以顺便学一下博弈论的sg函数。本题还需要记忆化搜索和状压。建议写完第六章回过头来补这个题。
ZJU1042-W’s Cipher:还是凯撒密码,稍微复杂点,可以试试。
ZJU1051-A New Growth Industry:纯模拟,可以练手。
ZJU1056-The Worm Turns:本章第一道大型模拟题,可以试试,但是赛场说实话基础不牢还是没信心开这种大型模拟的。
ZJU1057-Undercut:并不推荐,很无聊的题。
ZJU1073-Round and Round We Go:循环数,很有意思。可以模拟试一遍然后写后面数学做法。膜北大大佬,硬是把模拟题写成数学题。%%%
ZJU1088-System Overload:约瑟夫环,不要模拟了,太水了,推一下后面公式然后写递归做法,书上for循环版本也行,但建议再试试递归。不过本题有个坑点是1号楼永远先断,所以序号建议自己推下。
ZJU1098-Simple Computers:学了计组可以试试本题。
ZJU1144-Robbery:纯模拟,可以试试。
ZJU1160-Biorhythms:同余定理,建议模拟试一遍后学学中国剩余定理,网上讲的感觉好复杂,实际就是两数加最小公倍数同余
a%m=b%m表示成a≡b(mod m)。本题中设 x≡p(mod 23);x≡e(mod 28);x≡i(mod 33);这样推出 p+=23满足(p-e)%28=0时p满足前两个方程,以此类推。
第3章字符串处理技巧题
本章前后关联性不大,建议跳过本章从第四章开始,学完第6章甚至第12章再回头补本章,因为acm处理字符串确实是一个麻烦,但近几年很少有题目会卡在输入格式与处理字符串上。建议现学现用来的实在,没必要专门开一章。如果你刷PAT那当我没说,高校机试确实有很麻烦的处理字符串问题。
本章待补——
第4章大整数运算技巧题
大整数永远是acm需要先迈出去的坎,如果你觉得带了个高精度板子就高枕无忧那就太naive了(嘲讽脸),本章题并不多,建议全部写一遍,熟练掌握高精乘即可。
HDOJ2940-Hex Factorial: 高精阶乘,保存结果打表即可。原书程序可以优化一下,详见HDOJ2940高精度阶乘优化版。
ZJU1205-Martian Addition: 进制20的高精度乘法。本章没什么好说的,可以参考洛谷高精度与模拟题单。
ZJU1210-Reciprocals: 建议独立完成。不过书上代码是假的,样例都过不了还ac?。思路没问题,但是去掉末尾0后精度long long远远不够,还需要大整数模拟。参考题解:ZJU1210-Reciprocals题解。
ZJU1962-How Many Fibs?:斐波那契大整数模拟。打表判断,书上思路很好倒着存补‘0’然后用strcmp比较。当然也可以用int型数组存结果然后自己写个比较函数。本题也建议独立完成。
第5章基本数据结构题
本章旨在熟悉基本的STL,虽然比赛时候能不用stl就不用stl,知道范围能用数组绝不用vector,但是还是有很多无关数据结构的题需要借助stl实现。本章题量仍很少,不熟悉STL的同学可以边学边写,熟练的就直接跳过本章。
HDOJ3682-To Be an Dream Architect: 给各个点编号即可,不断加入vector中然后去重,书上代码可以理解为给x轴赋权值n2,给y权值n,给z赋权值1,相当于独立编号。还不懂的同学建议百度一下哈希是什么。
ZJU1004-Anagrams by Stack: 本题出在本章难度显得略高,但递归一定是入门最需要克服的坎。本题dfs便是不断搜索的过程,初学递归往往难点之一是不知道递归的参数是什么,会递归但不会写递归。我也是学了很久才渐渐熟练会写递归。参数往往是每次前进一个状态的变化的量,更简单的找出其中参数是找到最终需要用来判断边界的量,每次传递下一个状态的参数即可。
ZJU1061-Web Navigation: 用到stack的简单模拟,不熟悉的试试stack用法,熟悉跳过即可。
ZJU1062-Trees Made to Order:了解组合数学应该是能看明白题解的,组合数学著名的三个数列:Fibonacci、Stirling、Catalan。建议不懂的小白学下组合数学,机器学习绕不过组合数学的。本题用到卡特兰数,不懂具体怎么分析是看不懂题解在说什么的,觉得难的同学可以先跳过本题,学完9章再回过头看其他博客学习卡特兰数用递归写。虽然网上确实没有适合新手的组合数学题解
ZJU1094-Matrix Chain Multiplication:还以为是动态规划,结果是简单递归。递归思路不难,就是每次把括号里两个矩阵算出相乘次数再转化成新矩阵,每次跳出括号不断累加。不过书上STL这个思路也很巧妙,相当于直接算成括号匹配了,用栈实现,本题类似逆波兰数,可以递归建树O(nlogn)也可以用堆栈实现O(n)算法。
ZJU1097-Code the Tree:说实话我也看不懂这个标程怎么实现的。但思路就是不把它当成树建图,而是当作无向连通图来存。然后不断输出最小的单节点,至于每次选中最小的点就用priority_queue实现,最小堆能在O(1)时间内求出最小数。本题值得一写。
ZJU1156-Unscrambling Images四叉树的递归,题目有点费解,看一遍很难明白题目在说啥。大致意思就是第一块输入压缩的四叉树编号, 第二个输入对应位置的颜色号。
第6章搜索算法题
刷完本章可以说算是入门半只脚了,如果只是想训练一下为了数模等等比赛训练自己的编程能力或者为了工作面试、保研复试机试等等刷完本章就没必要再看了,不如刷专门的oj。
另外本章的深搜广搜不管对于工作还是保研或是竞赛,这些都是基本功,本章题量较大,如果是新手建议像我一样全部写一遍,写完会发现代码能力提升很大。如果已经有基础了,只需要挑下列加粗的题目练一下就可以进入下一章了。另外搜索的复杂度较高,所以竞赛难点不在于递归而在于剪枝,务必注意本章的各种剪枝操作。
HDOJ1010-Tempter of the Bone:很典型的深搜加剪枝题,广搜只能得到最短路所以不行(不明白为什么的同学建议这里开始学简单的数据结构知识),奇偶剪枝也不难,写上总没坏处 。
HDOJ1016-Prime Ring Problem:同样用奇偶判别剪枝,因为数字序列从1开始,如果是奇数个连续的数,就,一定会凑出一个至少和大于4的偶数。
HDOJ1175-连连看:建议本题bfs和dfs都做一遍,虽然书上没有剪枝,但实际上本题可以简单剪枝一下,即转弯两次后如果和终点不在一条线上那就没有必要往下试了。可以试一下这个剪枝,只需要一个判断条件return就行。另外bfs和dfs是基本功,像本题这种难度,至少半小时之内写完改完bug然后AC.如果感觉不熟练多写几遍,当然dfs写法有很多,可以像书上for外标记点,for内判断,也可以for外return判断边界,for内到下一层标记等等,多敲敲就能手熟(真肌肉记忆
HDOJ1241-Oil Deposits:很经典(老) 的题,值得一试。
HDOJ1242-Rescue:本题思路比较巧妙,反向搜索。另外注意如果是求最短路类型的题,dfs的复杂度虽然理论上和没有优化的bfs一样,但因为有回溯,所以时间一般是不用优先队列的bfs时间的两倍,虽然dfs好写,但其实带优先队列priority_queue的bfs也没有那么难,多练就熟悉了。dfs和bfs时间复杂度O(n2),而最小堆优化的bfs是O(nlogn),比赛求最短路能用bfs就不要用dfs。
HDOJ1312-Red and Black:没有剪枝,和那道非常经典的油田题差不多,书上只给了dfs的代码,仍建议用bfs再写一遍。
HDOJ1539-Shredding Company:dfs,书上题解是标记空格位置然后迭代,递归每个起点即可。不过要注意的一点是题目说的是原数字不能有前导零,但是分割的数字是可以有前导零的。
参考最后一个样例:
6 1104
rejected
即可以分为1,1,0,4或1,1,04。所以没必要特判0能不能作为起点。
HDOJ2266-How Many Equations Can You Find:和上题类似,递归标记分隔变成加减号而已。同样分隔的数字可以有前导零,没必要特判。
HDOJ2952-Counting Sheep:这题应该出在前面,简单的连通块,相信如果刷完前面的题,那本道用dfs或bfs写都不会超过5分钟,有木有感觉进步很大(笑)。
HDOJ4403-A very hard Aoshu problem:比之前题稍难,思路就是枚举等号位置然后分别对两边进行搜索。本题的预处理很巧妙:
//ch[]为输入字符串
for (int i = 0; i < len; i++)
for (int j = i; j < len; j++)
for (int k = i; k <= j; k++)
a[i][j] = a[i][j] * 10 + ch[k] - '0';
//例如ch=1212
//则a[][]={
//1 12 121 1212
//0 2 21 212
//0 0 1 2
//0 0 0 2
//}
可以把 a [ i ] [ j ] a[i][j] a[i][j]理解为起点为i终点为j的数字。
ZOJ1002-Fire Net:dfs,不断试当前位置加一,所以只需要判断之前位置即可。题解的时间复杂度还是很高(才发现不会证,貌似是NP问题,但至少比O(n!)大,比O(2n2)小),但n最多为4,所以这么写完全够了,优化的话,我只能想到剪枝判断条件和寻找下一个位置可以优化,但没有更好的思路。本题类似八皇后问题。如果有更好的思路请踹我(评论私信均可)。
ZOJ1008-Gnome Tetravex:本题实际上是从左向右,从上向下填方格,每次只需要判断左边和上边方格是否满足。剪枝操作是记录重复的类型,没有这个优化会超时,即把格子的类型和数量记录下来,不用判断重复类型。
ZOJ1047-Image Perimeters:本题较简单,因为题目明确内部没有孔,意味着内部没有周长,只需要算外边周长即可,本题无优化。唯一会难考虑的点是如何计算周长,实际上只考虑外边,那么对每一个格子‘X’,搜索四个方向格子,如果是‘X’就继续搜,如果是’.‘边长就加一。然后搜索四个对角有无连通的’X’即可。所以我们再最外层加一圈’.'就很容易判断。如果你学图像处理的话就知道卷积图片时这种补零操作很常见简单的,需要记住这种做法。
ZOJ1075-Set Me:判断条件较复杂,但就是简单的枚举。
ZOJ1079-Robotic Jigsaw:本题不留坑,就是我不会写。。。。
ZOJ1084-Channel Allocation:着色问题,参考图的m着色问题:借用一张图说明:
节点可以看做站台,不同频道意味着不同颜色相邻,由于转发器位于水平面内,所以说这个图一定是一个二维的平面图。它不会像克莱因瓶那样跳到另一个四维时空内然后连接回来(哈哈哈原谅我这个颇大的脑洞)。所以说连线不会交叉,意味着这是二维平面,对于完全平面图来说,离散数学证明过四色问题,也就是说我们至多用四个颜色就能将图渲染的每个节点与其他节点颜色不相邻,感兴趣的可以搜一下这个问题,原本是由画地图时以颜色区分不同国家得到的这个问题。
ZOJ1091:原题本来为国际象棋的一道题,该题流传甚广,洛谷过河卒一题也是一种变式。需注意骑士移动是走日字步的,否则相邻走完全没必要尝试路径,直接有固定解。本书罗列了三种方法,dfs和bfs建议都尝试一下(很奇怪我用bfs写wa了,不知道没有考虑什么特殊情况。)另一种方法floyd算法,这个算法很简单,三重循环,即对节点i和j,如果有一个中间跳板节点k,能使i到k加k到j的距离小于i到j的距离,我们就替换掉这个距离,当然这是一种打表的做法,可以尝试。(虽然比赛用不到这么费时的算法)
ZOJ1136-Multiple本题一定要写!!!
关于分支界定算法可以参考分支界定算法,对本题来说解空间很多,但我们只需要得到一个最小解即可,按照升序数字尝试就一定能得到最小解。所以说bfs对每个节点依次下搜,形象考虑,即二叉树的每个下一层一定会比本层多二倍,所以越往下搜索越好事。而本题为m叉树的解空间尝试,利用广搜可以一层一层尝试。找到最小解就返回所以很快。另外本题的优化需要学会,因为我们要找到模N的最小公倍数,所以余数一定不会超过N的范围(0-5000),我们每次只需要判断这5000个余数曾经有没有被尝试过即可,这样搜索的次数就会大大减少。
另外不懂为什么要用余数除的建议看看模的性质。对本题来说:假如前缀数是M,前缀数M模N的余数是R(即 M m o d N ≡ R M mod N≡R MmodN≡R),我们在前缀M后补一位数字A再次尝试时:
即求 M ∗ 10 + A m o d N M*10+A mod N M∗10+AmodN的结果,而由于模的性质可得:
( M ∗ 10 + A ) m o d N = ( M ∗ 10 ) m o d N + A m o d N = ( R ∗ 10 ) m o d N + A m o d N = ( R ∗ 10 + A ) m o d N (M*10+A) mod N=(M*10)mod N +A mod N=(R*10) mod N+A mod N=(R*10+A)mod N (M∗10+A)modN=(M∗10)modN+AmodN=(R∗10)modN+AmodN=(R∗10+A)modN
ZOJ1148-The Game:唉,本章最后一题还是中规中矩的,终于结束了。有两个点需要注意:一是题目中步数segment指的是直道数而非经过的空格数。所以说最短路用bfs时需要把同一个方向的格子全部搜索完。二是本题的gets只能用c编译,如果用c++需要用cin.getline()函数读入数据,而且之前一定要有getchar(),否则cin.getline()会先读取缓冲区的换行符。
第7章动态规划算法题
本书排版算比较好的,动态规划需要前面递归分治的知识才能写。动态规划和递归两者都是把大问题分成类型相同的小问题,但动态规划子问题间解决方案相互联系,而递归子问题不影响。当然动态规划需要内存消耗大,所以经常用滚动数组,状态压缩等等方法优化。而搜索时间复杂度高,经常重复求相同的子问题结果,所以用数组记录,这就是记忆化搜索,可以看出两者殊途同归(比如著名的斐波那契数可以滚动数组dp,也可以记忆化搜索)。
HDOJ1087-Super Jumping! Jumping! Jumping!:很入门的一道动态规划!排版确实不错,但是书上题解完全错了,代码是对的,你可以看出题解和代码前言不搭后语。书上题解是最长递增子序列算法,而不是最大递增子序列算法。题解如下:
定义dp[i]为以a[i]结尾的最大递增序列和。
初始化: d p [ i ] = a [ i ] dp[i]=a[i] dp[i]=a[i]; 1 ⩽ 1\leqslant 1⩽ i ⩽ n \leqslant n ⩽n
如果 a [ i ] > a [ j ] a[i]>a[j] a[i]>a[j] 则 d p [ i ] = m a x ( d p [ i ] , d p [ j ] + a [ i ] ) dp[i]=max(dp[i],dp[j]+a[i]) dp[i]=max(dp[i],dp[j]+a[i]) ; 1 ⩽ 1\leqslant 1⩽ i ⩽ n \leqslant n ⩽n, 0 ⩽ 0\leqslant 0⩽ j ⩽ i \leqslant i ⩽i
HDOJ1231-最大连续子序列:本题公式虽然用b数组记录j结尾的最大连续和,但实际上只用了一个sum来不断更新,这实际上就是滚动数组优化。不懂的同学再看看博客讲解,往后写就会慢慢理解了。另外本题思路不难,但是题解好像很复杂的样子,实际上就是一直求前缀和,如果是正的就继续加,如果是前缀和是非正的,那说明不要前面的项,序列和才能更大。
HDOJ1257-最少拦截系统:题解写的很清晰,我们更新每个系统拦截的最小的,如果比其中的小那就更新,如果没有那就另开一个系统。这样系统数一定是最小的(想想为什么)。
HDOJ1505-City Game:这道题排版极其不合适,应该排到下一题,有了1506的基础才好写本题。本题相当于把二维的图转换为一维的1506题。对于每一行,求面积最大的值。本题转换思路很巧妙。
HDOJ1506-Largest Rectangle in a Histogram:好题!比赛见过类似的题,想了很久无果,居然看见原版题了!本题思路:枚举每一个高度计算面积,难点是确定该高度究竟能扩展到多宽?确定边界需要动态规划的思想,我们分别确定每一个高度的左右边界。比如左边界,我们用dpl[i]表示第i个高度的左边界。第一个已经不能再往左边扩展了,即dp[1]=1,而之后的高度如果比边界高度低(或相等),那说明还能继续往左扩展,那继续扩展到该边界高度的边界判断,右边界同理。然后枚举每一个高度去算面积即可,需注意高度乘宽度的值需要long long型保存。
HDOJ1520-Anniversary party:树形dp入门题,没有上司的舞会,最简单最基本的树形dp,写完可以再试试洛谷的树与图上的动态规划,其中二叉苹果树也是很经典的一道树形dp。
HDOJ1864-最大报销额:01背包问题,先排除不符合要求的发票,然后直接代一维数组dp的公式即可。本章需要详细了解原理,建议看其他参考书或者博客弄清楚原理。这里推荐一个最简单的01背包入门,带图理解会很清楚:01背包问题。不过这个讲解的是最基本的二维数组dp,本题是优化的一维数组dp。背包问题有个很有名的博客:背包九讲。其中第一讲 01背包问题建议参考。
HDOJ1950-Bridging signals:很经典的LIS,网上讲解很多,不做说明。
待补~(逃
更新线
不定时更新,给赞催更。
由于期末临近以及寒假颓废所以咕咕了一个多月没有写题,不过我会在这个寒假补完其他题的~。