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≤cM,满足aici的个数等于满足bjcj的个数)。假设机器人的手很大,可以捧着任意多面国旗。如图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≤ab≤106,2≤a+bn

习题12-9 寻找缩图(Find a Minor, Beijing 2007, UVa1690)

对于无向图G,缩边e的操作是这样的:假定e的两个端点为uv,用一个新结点来代替边e,然后把原先关联到u或者v的边(除了e之外)改成关联到这个新点。执行一次缩边操作后,新图比原图少一条边(注意,新图可以有重边)。如果图H可以由图G经过一次或多次删边、缩边和删除孤立点操作后得到,则称HG的缩图。

缩图在图论中扮演着重要角色。例如,一个无向平面图要么有缩图K3,3(两边各3个结点的完全二分图),要么有缩图K5(5个结点的完全图)。

给一个包含V(3≤V≤12)个结点的简单无向图G,你的任务是判断它是否含有某个形如Kn,mKn(1≤n,mV)的给定缩图。

习题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。另外,不相关的历史事件必须按顺序讲,即如果有两个不相关事件ijij之前发生,则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) 仔细分析后可以发现:因为流量可以复用,所以其实复杂度连On)都不需要乘。不过对于本题的规模,这个优化不是必需的。

(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步。