1、问题的解决与超越由 NOIP2003 引发的思考2004 年 1 月问题的解决与超越由 NOIP2003 引发的思考信息学竞赛的目的是对有才华的青少年起到激励作用,促其能力得以发展。因此,信息学竞赛和当今教育的许多不同方式是一致的,它的价值不止于问题的解决和答案,更在于思考的过程。透过竞赛试题,看到题目的本意,找到问题的解决方案,更找到超越问题的思想和灵感,让思维的火花永远闪烁,青少年才能充满活力,我们的竞赛才能永葆青春。刚刚过去的 NOIP2003,结束的是这项赛事,思想的活动依然持续。四道题目,显示了竞赛日益灵活的方向,仅仅套用经典算法,或是仅仅记住某个标准程序绝不能适应竞赛的发展。如何正
2、视问题、理解问题、解决问题、透视问题、超越问题,就从NOIP2003 谈起吧。一、NOIP2003 题目概况题目名称 问题关键 推荐算法 推荐时效 优化及扩展神经网络(Network)确定处理的顺序宽度优先搜索线性的 已达到理论下限侦探推理(Logic)判断每句话的真伪枚举+判断 O(mp|Day|) 最好可以到达线性加分二叉树(Tree)最优化原理 动态规划 O(n3) 值得进一步探索传染病控制(Epidemic)寻找可行的近似算法随机化 无法估计 值得进一步探索二、NOIP2003 题目解析题目 1:神经网络(Network)题目描述 略题目分析理解问题的第一步就是认真“读题” 。那么我们
3、先来看一看这个题目涉及的问题。研究一下题目中所给的图的一些性质,可以发现如下特点:1 图中所有的节点都有一个确定的等级,我们记作 Level(i)2 图中所有的边都是有向的,并且从 Level 值为 i-1 的节点指向 Level 值为 i的节点我们不妨将其抽象为“阶段图” 。更一般地,我们可以发现所有的阶段图都是有向无环的,这样我们可以通过拓扑排序来得到期望的处理节点的顺序。问题关键确定处理节点的顺序。可行算法由于阶段图的性质使得该图的所有边所连接节点的等级都是相邻的,因此就可以设计出一个基于宽度优先搜索(即 BFS)的算法:1 初始时将所有输入层的节点放入队列;2 取出队列中的一个元素,不
4、重复地扩展并处理该节点所发出的边的目标节点;3 如果队列非空,则转向 2;4 输出输出层中所有满足条件的节点。但是由于本题在问题描述中并没有明确的给出判断一个节点是否是输入节点,因此需要在算法进行的过程当中额外地考虑一些边界情况的数据(这个过程即便是真实数据没有这样出也是要有的) ,下面给出的更一般的算法可能会更好的跳过这些边界情况。1 对原图中所有的节点进行一次拓扑排序;2 按照拓扑顺序处理每一个节点;3 输出输出层中所有满足条件的节点。时间效率线性的, 已是理论下界。题目 2:侦探推理(Logic)题目简述略问题关键如何判断每句话的真伪。可行算法这道题的关键点是“如何能够快速正确实现出来”
5、 ,事实上这道题对编码能力的要求要大于对算法本身的要求。由于这道题的数据范围并不是很大,但需要进行“字符串处理”这种比较麻烦的工作,因此在比赛时就可以采用效率低一些的枚举算法来换取编码上的简单。推荐的算法分为两步:1 预处理每个人的每一句话,并把它们分类处理;2 枚举罪犯和当前星期几,找出所有可能发生的情况。下面我们来逐步细化一下每一步的算法,对于第一步,我们希望的是把一些杂乱的不好处理的“字符串信息”转化为相对比较好处理的信息。为此,我们可以通过把“信息”进行分类的方法使得对于每一类信息,更加方便的处理(即我们可以用一个或者几个变量来表示) ,由题目描述可以发现语句可分为三类:1 指明 i
6、是否是罪犯的语句;2 指明今天是星期 d 的语句;3 没有意义的语句 (不符合格式要求 )。我们必须要说明的是任何不符合格式要求的语句都将被划分到第三类中去,这样在处理每个语句的时候就必须要考虑该语句是否符合格式要求,通过以上的处理,我们对于每一个语句用几个变量就可以表示了。对于第二步的细化,我们在枚举完罪犯和当前星期几之后,就可以比较方便的判断每一句话的真伪了,这样我们再根据每个人所说的话把人进行分类。1 没说任何一句有意义的话;2 只说真话;3 只说假话;4 既说真话也说假话。需要注意的是,对于第一类人我们既可以把他当成说真话的,也可以把他当成说假话的,而如果第四类人存在的话,那么从他本身
7、就可以推出矛盾了。最后,如果对于罪犯 i 存在一个 d 使得当前情况是可能的,我们就说 i 就是可能的罪犯。时间效率O(MP|Day|) (其中 Day=Sunday,Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)算法优化上面的算法对于比赛而言在时间效率上是完全可以接受的,但是对一道值得研究的问题,我们是否需要对算法进一步的优化呢?答案是肯定的,因为对于任何一个问题,只要当前算法的复杂度并未匹配理论的下限,就有再继续研究的价值。要么当前的复杂度下限可以向上更加精确地估计一些,要么当前的算法可以进一步地优化。下面我们就来看一个比较容
8、易想到的优化。我们可以发现在对罪犯和当前星期几进行“双重枚举”时,进行了很多重复的操作,于是我们想到,能不能不枚举是当前星期几?这样我们把这类语句与判断罪犯的语句分离,可以先由判断罪犯的语句中确定一部分人肯定说真话,一部分人肯定说假话,剩下的一部分人就要根据他所说的当前星期几的语句来判断了,首先我们假设所有人判断星期的语句不自相矛盾,这样每个人将在判断这类问题里面至多有一个答案,我们便可以统计判断当前是星期 d 的总人数,于是改进后的算法对于每一个可能的罪犯,先用 O(p)的时间处理所有的话,再用 O(|Day|)的时间枚举星期几。这样,改进后算法的复杂度就是 O(m(p+|Day|)。那么我
9、们可不可以再进一步,把算法优化到线性?这里面可以比较明确地告诉大家,是可以做到的,具体的算法类似于上面的按照语句的种类分离语句,只是分离得更细,处理得更复杂,在这里就不做赘述,留给大家思考。题目 3:加分二叉树(Tree)题目描述略问题背景题目描述的是一棵树,而这棵树的中序遍历是 1,2,n,我们把这样的树称作二分检索树(或排序二叉树) ,这样我们就由此想到了一个动态规划的经典模型最优二分检索树,描述如下:给出按照它们的主值排序的待检索的元素 1, 2, , n,以及它们被检索的频率f1, f2, , fn,要求你构造一个二分检索树,使得 最小,这iidepthf1)(里面 depth(i)表
10、示 i 节点在树中的深度。在动态规划解决这个问题时,我们通过把问题转换,最终得到了 ei,j=minitj | wi,j+ei,t-1+et+1,j的转移方程(这里面状态 i,j 表示中序遍历为 ij的子树,wi,j 表示这棵子树的权值和, ei,j表示这棵子树的最优值) ,由于一棵最优二分检索树根节点的左子树和右子树也都一定是最优的,因此这个满足最优化原理。问题关键最优化原理可行算法我们先来看一看最优化原理对于这道题是否适用呢?答案是肯定的,当我们试着写出状态转移方程:fi,j=maxi=t=j | dt+fi,t-1ft+1,j(这里面 di表示节点 i 的加分,fi,j表示最优状态),显
11、然如果 fi,t-1不是最优的那么我就可以构造出更优的 fi,j来,因此这道题也是满足最优化原理的。类似地我们可以写出一个动态规划的算法,我们可以按照状态 fi,j中 i 与 j的距离来划分阶段(当然也有其它的划分阶段的办法) ,先处理掉边界情况(空树和只含有一个节点的树) ,然后就可以按照阶段自底向上进行动态规划了,最后再递归地输出答案就可以了。时间效率O(n3)算法优化我们回想起在解决最优二分检索树的问题时,曾经利用单调性得到 rooti+1,jrooti,jrooti,j-1(其中 rooti,j表示中序遍历为 ij 的子树取道最优值时的根节点) ,这样把 rooti,j的取值范围进行了
12、一下限制,使得在一个阶段内,所有需要考虑的决策不超过 2n 个,于是总的复杂度就被降低到 O(n2)。那么我们是否可以也用类似的办法取优化这道题呢?这里不对优化的可行性作出讨论,不过可以肯定的是rooti+1,jrooti,jrooti,j-1是不一定成立的,一个显然的反例就是当 d1=d2=d3=1 时,root1,2=1或 2,root2,3=2 或 3,root1,3=1 或 3,那么无论 root1,3是 1 还是 3 不等式都总将有一边不成立。那么能不能跳过这种边界情况,寻找新的优化方案,仍然是一个值得探讨的问题。另外我们应该跳出固定算法的思维框架,想到最优二分检索树也是可以用贪心思
13、想来解决的,那么这道题能否类似地解决呢?这也应该是这道题的一个研究方向,不过总的来说,研究这道题的时候应该遵循题目本身的特点,不可生搬硬套。题目 4:传染病控制(Epidemic)题目描述略问题背景由于在对这道题进行了一段时间的思考以后,发现很难找到一个多项式级别的算法,因此猜想此题可能是一道 NP 完全问题,我们先来给出复杂性理论的一些定义:P(Polynomial)问题:存在解决该问题的多项式级别的确定性算法的问题。NP(Nondeterministic Polynomial)问题:如果对于该问题,对于每一个解答,存在多项式级别的确定性算法判断该解答是否可行,就称该问题是 NP 问题。例如
14、,哈密尔顿环游问题就是一个 NP 问题。NP 完全问题:能够等价转化为哈密尔顿环游问题的问题。这样我们可以知道所有的 NP 完全问题都是可以等价转化的。关于 NP 完全问题是否存在一个多项式级别的确定性算法,至今仍是一个悬而未决的问题。因此对于这道题而言,跳出对这样算法的追求,寻找一个近似算法,或者寻找一个时间效率说得过去的搜索算法不失为一个好的对应策略。问题关键理解问题本质, 找出可行的近似算法可行算法我们首先来通过对样例数据和其它的一些比较简单的数据进行一些观察,很容易得到一种贪心的算法,对这种算法可做如下的描述:1 我们用 Sum(i)表示以 i 为根节点的子树上的节点总数。那么我们每次
15、砍掉当前层中还未砍掉的节点里面使得 Sum(i)取到最大值的那个节点;2 重复第 1 步直到当前层中的节点为空。这样在我们构造的许多数据中,这个算法似乎总是能得到正确的结果。那么这样的算法是否就令人满意呢?在算法的正确性没有被彻底证明之前,尝试去找一些反例总不失为一个好的习惯,比如说对于这个贪心算法我们就能找到如下一个反例。在这个图里面如果用上面贪心的法则来进行的话,那么最终被截掉的边我们用粗线表示,可以看到在这种策略的选择下我们最终将保留 3 个节点,而用虚线表示的最优策略将使我们最终只剩下 2 个节点。既然反例已经被找到,我们就应该意识到是算法本身的缺陷造成了这种反例的存在。那么我们该如何
16、弥补算法的这一缺陷呢?应该看到,贪心算法对于这道题的大多数数据都能得到比较不错的结果,但是贪心法本身的固有缺陷使得一旦作出了错误的决策,就没有希望得到正确的解。这样我们便容易想到能否通过尝试做出多次看起来正确的决策并在它们中间选取一个最优的来进行我们的近似算法。由此思路孕育而生的就是在贪心选择中引入随机化因素而得来随机化算法,这个算法可被描述如下:1 我们通过随机函数来选择即将被砍掉的节点,可以通过设置权值来使得算法更大可能地去选择 “看起来最优 ”的决策;2 重复第 1 步直到当前层中的节点为空;3 对前两步执行多次并选取所得到的最优结果。这个算法被设计出来以后,就很难在构造出能使这个算法失
17、败的数据了,因此在考场上能够设计出来这个算法就已经很令人满意了,而事实也恰恰如此(这个算法可以通过所有的测试数据) 。另外,我们在构造数据的过程中也发现,似乎很难构造出不确保是多项式级别的搜索算法低效的数据,因此比赛时选择搜索算法也不失为不错的策略,这里对该算法就不在作赘述。问题扩展在尝试进一步研究这个问题的时候,我们遇到了以下的困难:1 能否找到一个多项式算法或者证明此题是 NP 完全?2 怎样构造数据 , 使得解决此题的近似算法或搜索失败?3 对于这样的数据 , 怎样修改算法 , 使得算法可以通过这些特殊的数据?上面的三个问题制约了对该算法的进一步研究,但是同上一道题目一样,本题仍然是一道
18、值得研究的题目。三、NIOP2003 题目之外1. 分析问题拿到一个题目,在“读题”的阶段尝试把题目引入的概念抽象出来,并联想这些概念相关联的背景和经典模型,不仅能够使算法的设计上得到一些启发,而且对于准备评估问题的难度,寻找可行的比赛策略也是有一定帮助的。而这个环节需要长时间对经典问题和经典算法进行积累,这是一个需要长期训练的过程。同时我们也应该看到,题目本身与该题目所在的经典模型中本质上可能存在着一些本质上的区别的,如在加分二叉树那道题中分析的那样,该题与经典的模型同样是排序二叉树,同样是使某个函数取到最优值,但是最优二分检索树中一个用来降低复杂度的结论对于这道题而言却可以找到反例,这就需
19、要我们在设计和优化算法的时候,能够抓住问题本身的特点,正确认识它与经典模型的相似之处与不同之处。这样,就可以使设计出来的算法正确而有效。2. 解决问题对于一种可行的算法,如何正确并快速的实现出来?我们也可以看到,对于有些题目,解决的重点往往不是对算法的设计上,而是如何在把题目中比较杂乱的信息以一个较简洁的方式实现出来。做到这一点,不仅要对自己的编码能力有一个正确的评估,而且对比赛策略的总体把握上,也是需要进行一番思考的。换句话说,就是在选择算法之前,应先想想算法的时间效率是怎样的?这样的算法的实现复杂度是否能接受?题目中能接受的最坏时间效率是怎样的?能否简化一些东西来降低编码的复杂度?3. 超
20、越问题比赛结束后,如何考虑是否能对比赛时的算法进一步地优化,是否能对问题进行扩展?对于一道值得研究的题目来讲,我们不应该仅仅满足于在比赛中将它做出来并且得到很高的分数。适当地对题目的算法进一步优化以及做一些扩展,对于更深入地挖掘这个问题,增加其利用价值,是有很大帮助的。在分析侦探推理那道题目的时候,我们在实现了一种比较容易实现的算法之后,发现算法的复杂度远远没有达到理论下限,进而我们又意识到,这个算法存在大量的重复的判断,于是我们通过分离枚举的元素,避免了一部分重复的发生,使得复杂度得到了一定程度上的优化,运用这个办法同样也可以将算法优化到线性。对一道题目的扩展同样很有可能比原问题困难得多,比如在尝试扩展传染病控制这道题目的时候,无论在理论上还是在实践上都遇到了很大的困难,这样我们就通过扩展原问题得到新的问题(当然也有可能是非常难解决) ,从而更大程度地挖掘问题本身。参加信息学奥赛是做一次思维体操,如何对待问题、解决问题、超越问题,本文提供了一种可以参考的基本策略,当然这些东西往往因人而异,仅希望抛砖引玉,引发更多人更深层次的思考与探索!