12.3 小结与习题
至此,本书内容已经全部讲完。仔细看完本章的读者想必已经掌握了《算法竞赛入门经典》和《算法竞赛入门经典——训练指南》中最精髓的部分,在理论和实践上都相当有经验了。按照惯例,下面是例题列表,如表12-10所示。
表12-10 例题列表
| 类别 | 题号 | 题目名称(英文) | 备 注 |
| 例题12-1 | UVa1671 | History of Languages | DFA |
| 例题12-2 | UVa1672 | Disjoint Regular Expressions | 正规表达式;NFA |
| 例题12-3 | UVa1673 | str2int | DAWG(或后缀自动机) |
| 例题12-4 | UVa12161 | Ironman Race in Treeland | 树的分治 |
| 例题12-5 | UVa11994 | Happy Painting | Link-Cut树 |
| 例题12-6 | UVa1674 | Lightning Energy Report | 树链剖分或LCA |
| 例题12-7 | UVa12538 | Version Controlled IDE | 可持久化treap |
| 例题12-8 | UVa805 | Polygon Intersections | 多边形交 |
| 例题12-9 | UVa1675 | Kingdom Reunion | 扫描法;DSLG |
| 例题12-10 | UVa12314 | The Cleaning Robot | 多边形偏移 |
| 例题12-11 | UVa1520 | Flights | 嵌套线段树;扫描法 |
| 例题12-12 | UVa1676 | GRE Words Revenge | 数据结构的组合;分层数据结构;DAWG的综合应用 |
| 例题12-13 | UVa11998 | Rujia Liu Loves Wario Land! | 启发式合并;树链剖分的综合应用;块链表 |
| 例题12-14 | 1UVa1104 | Chips Challenge | 网络流建模 |
| 例题12-15 | UVa12567 | Never7, Ever17 and Wa[t]er | 线性规划 |
| 例题12-16 | UVa12110 | Gargoyle | 特殊费用流或线性规划 |
| 例题12-17 | UVa12253 | Simple Encryption | 数论;数学猜想 |
| 例题12-18 | UVa12164 | The Great Game | 马尔科夫过程;二分法(或不动点迭代) |
| 例题12-19 | UVa1677 | Cycling | 数形结合;对最优解性质的分析 |
| 例题12-20 | UVa1678 | Huzita Axiom 6 | 解析几何;三次方程 |
| 例题12-21 | UVa1679 | Easy Geometry | 凸函数 |
| 例题12-22 | UVa12162 | Shooting the Monster | 离散化 |
| 例题12-23 | UVa1017 | Merrily, We Roll Along! | 模拟或离散化 |
| 例题12-24 | UVa1286 | Room Services | 几何猜想;动态规划 |
| 例题12-25 | UVa1288 | Shortest Flight Path | 球面几何;区间覆盖;简单图论 |
| 例题12-26 | UVa12565 | Lovely M[a]gical Curves | NURBS曲线;近似算法 |
| 例题12-27 | UVa11188 | A Strange Opera House | 几何计算;暴力法 |
| 例题12-28 | UVa12308 | Smallest Enclosing Box | 旋转卡壳;近似算法 |
| 例题12-29 | UVa1680 | Journey | 递归;记忆化搜索;绝对值的处理 |
| 例题12-30 | UVa1097 | Rain | 最短路;图遍历 |
| 例题12-31 | UVa1681 | Dictionary | 字符串和图论综合题 |
| 例题12-32 | UVa11199 | Equations in Disguise | 搜索;优化 |
| 例题12-33 | UVa1682 | Exclusive Access | 互斥算法验证;找圈 |
| 例题12-34 | UVa11521 | Compressor | 复杂动态规划 |
| 例题12-35 | UVa12417 | Formula Editor | 复杂模拟题;OOP |
| 例题12-36 | UVa12666 | Killer Puzzle | 复杂模拟题;Lisp |
| 例题12-37 | UVa12720 | Mysterious Space Station | 算法综合题;交互式题目 |
由于篇幅限制,上述内容无法全部详细地介绍给读者。请读者以“可持久化数据结构”、“后缀自动机”、“动态树”等关键字在网上搜索,能获得很多详细、实用的资料,包括讲解、代码和更多精彩例题。另外要强烈推荐的是MIT的6.851课程:高级数据结构(Advanced Data Structures),2012年的课程主页是:http://courses.csail.mit.edu/6.851/spring12/。
然而,知识是永无止境的,高水平的竞赛中还有许多本书以《训练指南》中没有涉及的知识、技巧和题型。表12-11中将列举新知识点以及相关题目,以供参加高水平竞赛的选手查漏补缺。
表12-11 新知识及相关题目
| 题号 | 题目名称(英文) | 备注 |
| UVa1683 | In case of failure | 可以用Delaunay三角剖分或者k-d树 |
| UVa12629 | Rectangle XOR Game | Nim积 |
| UVa12698 | Safari Park | 梯形剖分 |
| UVa12711 | Game of Throne | 任意图最大权匹配(实现最基本的Edmonds算法即可) |
| UVa12713 | Pearl Chains | Delannoy数;Lucas定理 |
| UVa12513 | Safe Places | 三维凸包;多面体的交 |
| UVa11594 | All Pairs Maximum Flow | Gomory-Hu树 |
| UVa12415 | Digit Patterns | NFA转DFA(动态) |
| UVa11993 | Girls' Celebration | PQ树 |
| UVa10766 | Organising the Organisation | Matrix-Tree定理 |
| UVa11118 | Prisoners, Boxes and Pieces of Paper | 非常精彩的题目。虽然没有什么扩展性,但是强烈推荐 |
| UVa11915 | Recurrence | 钩子公式 |
| UVa1684 | Escape Plan | K短路(结点可以重复经过) |
| UVa1685 | Enjoyable Commutation | K短路(结点不能重复经过) |
下面的习题不一定可以用来练习本章中介绍的各种知识点和技巧,也不一定有很高的难度。在这里把它们翻译出来,只是因为笔者比较喜欢这些题目,希望能与读者分享。
习题12-1 自编SketchUp(My SketchUp, Rujia Liu's Present 4, UVa12306)
Google SketchUp是一个很棒的软件,可以用来创建、修改和分享3D模型。在本题中,你需要编写它的一个2D简化版,即My SketchUp。
My SketchUp的使用非常直观。例如,画两条交叉线段后,两条线段会被自动截断成4条,因此在图12-75(a)中单击小圆点后只会选中一条线段(粗线部分),删除后如图12-75(b)所示。此时单击图12-75(b)中的小圆点,会选中另一条线段。把该线段删除后剩下的两条线段会自动合并成一条线段,如图12-75(c)所示。另外,在任何时候,重复的线段都会合并成一条。
|
| |
| (a) | (b) | (c) |
图12-75 自动分裂和合并线段
换句话说,对于一个图形来说,它的“长相”决定了它的实际结构,与“这个图形是如何画出来的”无关。一个图形看上去是什么样的实际就是什么样的。例如图12-76包含14个顶点和15条线段。
图12-76 图形示例
输入是n≤100条DRAW和REMOVE语句,输出是图形中的各个点的坐标和各条线段两端的点编号,按照字典序排列。DRAW的参数一条折线(最多包含20个点),而REMOVE语句有3个参数x y d,功能是删除离(x,y)的距离不超过d的所有线段。
评注:这是一道很考验编程能力的题目,稍不注意就会让程序变得很复杂而且非常容易出错。
习题12-2 平铺(Tiling, ACM/ICPC Jakarta 2012, UVa1686)
输入6个整数DX1,DY1,DX2,DY2,DX3,DY3,……(绝对值均不超过10000),所有可以写成(iDX1+jDX2+kDX3, iDY1+jDY2+kDY3)的位置都有一个点,如图12-77所示。
图12-77(a)是一个周期,图12-77(b)是铺贴方法。你的任务是求最小周期。
|
|
| (a) | (b) |
图12-77 平铺问题示意图
评注:本题的结论就是一个简单公式,但是得到这个公式却不容易。
习题12-3 切片树(Slicing Tree, ACM/ICPC Daejeon 2012, UVa1687)
有n(1≤n≤1000)个矩形的长宽值和一棵切片树,要求把矩形按照切片树的规则摆放,使得最小包围盒面积最小。如图12-78所示,切片树是一棵二叉树,每个叶子代表一个矩形,每个内结点是H或者V,表示左子树中所有矩形位于右子树中所有矩形的下方/左方。注意:矩形可以横放也可以竖放。
图12-78中是一棵切片树和符合该树的两种摆放方法。
图12-78 切片树和两种摆放方法
习题12-4 虫洞(Wormhole, ACM/ICPC NWERC 2009, UVa12227)
科幻小说里常提到虫洞。所谓虫洞,就是一个可以把你传送到遥远地方的东西。更神奇的是,虫洞还能带你到过去或者未来。
在本题中,假定空间里有n(0≤n≤50)个虫洞,你的任务是在时刻0从起点出发,借助这些虫洞在最早的时刻到达终点。每个虫洞用入口坐标(xs, ys, zs)、出口坐标(xe, ye, ze)、创建时间t和时间偏移d来描述(|t|,|d|≤106)。当你在t时刻或更晚时刻到达入口时,将会转移到出口,并且当前时刻加上d(当d为负时,相当于时光倒流)。坐标均为绝对值不超过10000的整数,且所有点都不相同。
提示:本题并不是特别难,但很有启发意义。
习题12-5 屋顶(Roof, Seoul 2005, UVa1688)
给一个边平行于坐标轴的多边形P,所有边同时向内以相同速度收缩,并且以这个速度向上(+Z)移动,最终得到一个屋顶,如图12-79所示。求屋顶的高度。
|
| |
| (a) | (b) | (c) |
图12-79 屋顶
提示:方法不止一种,且复杂程度差异较大。
习题12-6 国际活动(International Event, ACM/ICPC Daejeon 2013, UVa1689)
有一个盛大的国际活动,一年举办一届。在活动现场,有N(2≤N≤100000)个旗杆排成一行,每个旗杆上都有一面国旗迎风飘扬。
每个旗杆用3个数li, ai, bi表示,即旗杆的坐标为li,去年挂着国家ai的国旗,今年需要换成国家bi的国旗。你有一个机器人,初始位置为A,要求为机器人设计一条路线,把所有旗杆上的国旗换成今年的,且移动总距离最小。
国家编号为1~M(1≤M≤1000),且每个国家的国旗至少挂在一个旗杆上,并且去年和今年的旗杆数不变(即对于任意1≤c≤M,满足ai=c的i的个数等于满足bj=c的j的个数)。假设机器人的手很大,可以捧着任意多面国旗。如图12-80所示,每个旗杆用两个数(ai,bi)表示,箭头表示了最优路径:4-5-1-7-4。
图12-80 旗杆及最优路径
习题12-7 拿行李(极限版)(Collecting Luggage EXTREME, UVa11425)
有一个n(n≤100)边形传送带,上面有你的行李。已知你和行李的初始位置、传送带移动的速率和你行走的最大速度,求拿到行李的最短时间。
评注:本题是ACM/ICPC 2007世界总决赛中一道难题的加强版。原题规定人的速度大于传送带移动的速度,因此可以二分。原题的详细分析参见《算法竞赛入门经典——训练指南》。
习题12-8 加速器(Accelerator, ACM/ICPC Daejeon 2011, UVa1570)
图12-81 “加速器”问题示意图
圆周上等距排列着n个点,其中有a个红点(用圆形表示)和b个蓝点(用方形表示),要求每个红点配一个蓝点,每个蓝点最多配一个红点,使得连线的总长度最小。两个匹配点的连线长度等于二者的劣弧长度。例如图12-81中的最优解为:位置1,3,9的红点分别匹配位置5,4,10,连线长度为6。所有红蓝点位置均不同。1≤n≤106,1≤a≤b≤106,2≤a+b≤n。
习题12-9 寻找缩图(Find a Minor, Beijing 2007, UVa1690)
对于无向图G,缩边e的操作是这样的:假定e的两个端点为u和v,用一个新结点来代替边e,然后把原先关联到u或者v的边(除了e之外)改成关联到这个新点。执行一次缩边操作后,新图比原图少一条边(注意,新图可以有重边)。如果图H可以由图G经过一次或多次删边、缩边和删除孤立点操作后得到,则称H是G的缩图。
缩图在图论中扮演着重要角色。例如,一个无向平面图要么有缩图K3,3(两边各3个结点的完全二分图),要么有缩图K5(5个结点的完全图)。
给一个包含V(3≤V≤12)个结点的简单无向图G,你的任务是判断它是否含有某个形如Kn,m或Kn(1≤n,m≤V)的给定缩图。
习题12-10 赌博(Hey, Better Bettor, ACM/ICPC World Finals 2013, UVa1573)
你在赌场上玩一个游戏,每次的赌注是1美元,赢了会得到2美元,输了什么也得不到。赌场有一个优惠:在任何时候,赌场可以补偿x%的损失。使用优惠之后你可以继续玩,也可以退出赌场。退出赌场之前最多只能使用一次这样的优惠。
例如,x=20,你玩了10次,赢了3次,总共损失10-3*2=4元,使用优惠后损失3.2元。但如果你赢了6次,总共获利6*2-10=2元。
假定每局比赛获胜概率为p%,输入x, p(0≤x<100,0≤p<50),输出最优策略下最大的期望获利。
提示:本题和“伟大的游戏”一题有些相像,但也有区别。
习题12-11 完全平方子集(Hip To Be Square, ACM/ICPC NWERC 2012, UVa1691)
6,10,15均不是完全平方数,但是它们的乘积900是完全平方数。输入两个整数a,b (1<a<b≤4900),找{a,a+1,…,b}的一个非空子集,其所有元素的乘积为完全平方数k2,要求k尽量小。输入保证答案小于263。无解输出none。例如,20 30的解为5,101 110的解为none, 2337 2392的解为3580746020392020480。
提示:本题的方法并不优美,所以请使出浑身解数吧。
习题12-12 米诺陶洛斯的迷宫(Labyrinth of the Minotaur, ACM/ICPC NEERC 2012, UVa1692)
输入一个宽为w、高为h(2≤w,h≤1500)的矩形迷宫,左上角(1,1)是出口,右下角(w,h)是怪兽。放一个尽量小的正方形障碍(不能放在入口或者怪兽上)使得怪兽无法从出口出去。初始时保证怪兽和出口之间有通路。多解输出任意解,无解输出impossible。如图12-82所示,矩形是一个最优解,边长为2。
图12-82 最优解
提示:“太空站之谜”的题解看了吗?如果还没有,现在就看看吧。
习题12-13 XAR(XAR, ACM/ICPC Beijing 2006, UVa1693)
机器XAR08有n个(n≤128)8位寄存器,可以存储8位无符号整数,支持4种操作(每个操作都同时作用于所有寄存器):
给出n个寄存器的初始状态di(0≤di<128且各不相同),设计不超过40000条指令,使得执行后各寄存器的值分别为0,1,…,n-1。
习题12-14 收购游戏(Takeover Wars, ACM/ICPC World Finals 2012, UVa1290)
T公司有n(1≤n≤105)个子公司,B公司有m(1≤m≤105)个子公司。每个子公司有一个市场价值,均为不超过1012的正整数。
每次可以合并两个公司。合并同一个公司的两个子公司没有限制。合并之后市场价值等于合并前的两个公司之和。
每个公司都可以用己方的一个子公司A吃掉对方的一个子公司B,条件是A的市场价值严格大于B的市场价值。被吃掉的子公司B消失,而子公司A的市场价值不变。为了简单起见,假定任意操作序列都不会产生两个母公司且市场价值相同的子公司。
两个公司轮流操作,T公司先。如果无法操作,则再次轮到对手操作。你的任务是判断谁赢。
习题12-15 历史课(History course, ACM/ICPC CERC 2013, UVa1694)
给定n(1≤n≤50000)个历史事件,各用一个区间[ai,bi]表示,即事件的开始时刻和结束时刻。如果两个历史事件的区间有公共点,说明两个历史事件是相关的。我们需要给学生讲这些历史事件,其中每堂课讲一个事件。我们希望相关历史事件在排课时尽量排在一起,即要找一个最小的k,使得相关历史事件的课堂编号之差不超过k。另外,不相关的历史事件必须按顺序讲,即如果有两个不相关事件i和j,i在j之前发生,则i的课也必须排在j之前。要求输出任意解。
习题12-16 Quall[e]? Quale?(Quall[e]? Quale?, Rujia Liu's Present 6, UVa12570)
有n道题,每道题的标题有多语言版(一共有m种语言)。已知每道题的每种语言的版本以什么字母开头,要求前n个字母的题目各一道。问:实际用到的语言集合有哪几种可能?例如,有5道题,3种语言。每道题目的每种语言版开头字母如图12-83所示。
图12-83 题目不同语言版本的开头字母
一个合法解如图12-84所示。
图12-84 合理解法之一
实际用到的语言是{English, French, Chinese},3≤n≤26,1≤m≤5。
评注:本题可以用《训练指南》中介绍的DLX算法解决,也有实际效率更高的方法。
习题12-17 单后对单车(Queen vs Rook, UVa10383)
你的任务是解决国际象棋里的著名残局“单后对单车”。输入4个棋子的位置和下一个移动的棋子颜色。要求在第一行输出获胜方及获胜的最少步数,第二行输出下一次移动方的最优策略(若是必胜方,应输出获胜最快的策略;若是必败方,应输出失败最慢的策略;若是平局,输出导致平局的策略)。本题不允许后和车易位。
输入最多有1000组数据,保证任何两个棋子不会位于同一个格子里,并且后和车的颜色保证不同。不该移动的一方不会“已经被将死”,但是该移动的一方有可能“已经被将死”。输出中用X表示吃子,“+”表示将军,“#”表示将死。
评注:本题容易超时,需要优化,且有些优化本身的代码量比较大。
习题12-18 谱曲(Melod[y] "Creation", Rujia Liu's Present 6, UVa12566)
可以用字符串来表示一个简谱,其中小节线为“|”,s1=s2表示一个转调,即该音符在转调前是s1,转调后是s2。例如,下面的简谱是一个“诡异版”的生日歌:
5 5 6 5 1=4 3 | 1 1 2 1 5 4 | 1=5 5 5 3 1 7=3 2 | b7 b7 6 4 5=2 1 ||
输入一个简谱,要求将它改写,使得升降号不超过k个,在此前提下转调的次数最少。多解时,输出字典序最小的解。要求音符数不超过100。音乐知识和题目背景请参考原题。
习题12-19 大逃亡(Escape, ACM/ICPC CERC 2013, UVa1695)
有一棵n(1≤n≤200000)个结点的树,初始时你在结点1,生命值HP=0,目标是从结点t的出口逃出来。每个结点有一个怪兽或者一个鸡腿。当第一次到达一个结点时,你的HP会发生变化:打怪之后HP减少,吃鸡腿之后HP增加,改变量等于结点权值的绝对值,负数表示怪兽,正数表示鸡腿,0表示什么都没有。注意,如果终点t内有怪兽,必须先打怪兽然后才能逃出。问是否能成功逃出。
习题12-20 蜘蛛旅行家(Travelling Spider, ACM/ICPC Daejeon 2011, UVa1696)
把一个魔方的每个面分成n*n(2≤n≤50)的正方形,如图12-85所示(n=4)。不难发现,每个正方形恰好有4个相邻正方形。
图12-85 n*n正方形
在两个正方形的中心点分别放一只公蜘蛛和一只母蜘蛛,求一条路径,从公蜘蛛出发,经过所有正方形的中点恰好一次后到达母蜘蛛。换句话说,包括起点和终点,求出的路径应恰好包含6n2个互不相同的正方形,且路径上相邻的两个正方形在魔方上也相邻。
无解输出-1,多解输出任意解。
————————————————————
(1) 下面的方法称TCA (Thompson's Construction Algorithm)。
(2) 见A. Blumer等人于1985 年写的经典论文:《The Smallest Automaton Recognizing the Subwords of a Text》。
(3) 出于时间和空间上的考虑,在竞赛中我们往往不是给每条重路径建一棵线段树,而是用一棵全局线段树保存所有树链,限于篇幅,这里不再详细介绍。
(4) 原始论文:http://www.cs.cmu.edu/~sleator/papers/self-adjusting.pdf。这里介绍的版本和原始论文有差异,在实践中更为常用。
(5) 原论文中不是使用的伸展树,因为Link-Cut树比伸展树更早发明。
(6) http://en.wikipedia.org/wiki/Rope_%28computer_science%29。
(7) 它的正式名称为多边形偏移(offseting)。
(8) 《训练指南》中的“图询问”问题也用到了这个技巧。
(9) 仔细分析后可以发现:因为流量可以复用,所以其实复杂度连O(n)都不需要乘。不过对于本题的规模,这个优化不是必需的。
(10) 本题还有一个有意思的结论:最小费用对应的f一定是有理数,且分母不超过n(即滴水嘴的数量)。这个结论并不容易证明,有兴趣的读者可以一试。
(11) 事实上,还可以证明一个更强的结论:如果不考虑“K2不能有前导0”这个条件,K2是唯一存在的。
(12) 官方数据中的最大答案为1685.830。
(13) 大圆(Great Circle)是球面上半径等于球体半径的圆弧。连接两点的最短“球面线段”等于经过两点的大圆上的劣弧。
(14) 比赛中唯一通过此题的Anton Lunyov就是采用的这种方法。
(15) A Strange Opera House II, Rujia Liu's Present 4, UVa12309
(16) 题目来源:NOI2000,命题人:李申杰。UVa中的数据经过加强,难度大大高于NOI中的测试数据。
(17) 习惯上用A[x…y]表示子序列A[x],A[x+1],…,A[y],后同。
(18) 为了方便,还可以保存光标在每个级别的编辑框的元素指针。
(19) 它可以把代码压缩到5~6KB。而传统的OOP写法往往需要8~10KB。
(20) 这些批评也是有道理的。事实上,很多ACM/ICPC选手因为过于习惯编写独立、简短的代码,在工作初期会不适应大型软件的协作开发。
(21) 在软件工程领域,不同的遗留代码情况、团队情况以及软件的预计规模、需求变化情况等,都会影响到程序架构和设计决策。
(22) 相信看过《算法艺术与信息学竞赛》的读者对这个题目不陌生。
(23) 这是一个很特别的程序设计语言,看过《黑客与画家》的读者相信对它并不陌生。这个语言有不少吸引人的地方,但它的复杂程度却是大大超过普通人的预期。对此,笔者在实际项目的开发中已略有体会。有兴趣的读者可阅读《ANSI Common Lisp》入门,然后在《On Lisp》和《Practical Common Lisp》等经典著作中找到更多信息。
(24) 当然可以用STL 的string 来表示字符串。但是因为本题的字符串大都非常短,所以使用STL字符串带来的效率损失是比较明显的。
(25) intVal、strVal 等成员可以写成联合(union)的形式以节省空间,不过和本题的核心关系不大,这里就不叙述了。
(26) 这个设计也许会让scala 程序员会心一笑。另外,熟悉STL的读者也许会更倾向于复用STL中的functor。
(27) 题目来源:NOI冬令营2002。命题人:刘汝佳。
(28) http://olympiads.win.tue.nl/ioi/ioi99/contest/official/under.html。
(29) http://en.wikipedia.org/wiki/Maze_solving_algorithm#Wall_follower。
(30) 对于原题的10组官方数据,优化前的最坏情况需要走20000步左右,优化后只需不到2000步。