题目概述
一天,鬼谷子随意从2~99中选取了两个数。他把这两个数的和告诉了庞涓, 把这两个数的乘积告诉了孙膑。孙膑和庞涓彼此不知到对方得到的数。第二天, 庞涓很有自信的对孙膑说:虽然我不知到这两个数是什么,但我知道你一定也不知道。随后,孙膑说: 那我知道了。庞涓说:那我也知道了。
习题4:假设你是最近被捕的P个囚犯之一。监狱长是个疯狂的计算机科学家,他给出了以下启示:
找规律。第一张图和第二张图其实已经暗示了这道题目的潜在规律。注意到从一个第一行的点横向出发再回到这个点只有从这个点下面的点回来。定义F[i]表示从前i列中第i列的第2个点到第1个点共有多少合法路径,根据这个定义F[n]即为所求。G[i]表示前i列中第i列的第1个点到第4个点共有多少路径。因此可以推出F[i] = F[i-1] + G[i-1] (画图找规律)。
下面我们仔细观察这两个方程的定义,从而化简方程,如下图:
G[i] = F[i-1] * 2 + G[i-2] + G4[i] (1)
G4[i] = F[i-2] * 2 + G4[i-1] (2)
由(1)化简(2)式得到:G4[i] = G[i-1] - G1[i-1] - G2[i-1] - G3[i-1] + F[i-2] * 2 = G[i-1] - G[i-3]
所以G[i] = F[i-1]*2 + G[i-1] + G[i-2] - G[i-3] (3)
又由F[i] - F[i-1] = G[i-1] (4)
最终我们得到 F[n] = 2F[n-1] + 2F[n-2] - 2F[n-3] + F[n-4] (n>=5)
初始值:F[1] = 0; F[2] = 2; F[3] = 4; F[4] = 12。
其实边界需要统计的是上下左右4个方向没有被覆盖到的边长的总和。这个问题的难点是怎样使得需要判断检索重叠的复杂度尽量小,我们先简化问题,假设所有矩形都是A情况的,也就是宽度都一样并且垂直排列,这样的话我们就将矩形的情况分为不相交,交叠,包含几种情况。先从横向的边开始看,如图所示,如果只考虑蓝色和浅绿色这两个矩形因为没有任何与其相交的其他矩形,直接统计横向边长,然而交叠和包含要怎么办呢。如图红色和黄色的矩形有橙色的部分,那么也就是说我们只用统计红色的上边长和黄色的下边长了。而浅蓝色和紫色的关系也是类似只用统计紫色的两个边。
从这里我们得到一些启发,假设所有边长都是1个单位长度,显然我们只是统计一个覆盖域的上下两个边界,那么这从抽象意义上很类似括号匹配的问题,下面的这些矩形块可以抽象成()()(())(())注意观察它们的特点,可以发现跟真实的配对没有关系,也就是()()(())(()),我们用一个栈依次压入括号,每次出现()的配对就出栈。然后要统计这些,当压入后(且栈只有这一个元素)时计数器加一,当弹出元素后栈空时计数器加一。这样我们就相当于统计出了所有的该纳入计算的边界,由这个思考点出发,我们要对所有的矩形边进行离散化,同时要区别上下边界,抽象成“左右括号”。这个时候有个细节,对于横向边我们按纵标排序,如果纵标一样谁在前?其实这也是交叠的一种情况,如果纵标一样,先要把左括号排在前,也就是如果我们按纵标由小到大排,下边界应在前,不然会使得周长变大。
具体实现时我们看B图,因为配对不只是一种情况,而且坐标很大,这时候我们可以使用线段树来辅助数据结构的部分,需要横着纵着处理两次。也就是对于类似括号匹配中栈的操作,真正实现用的是线段树。
这样本问题就解决了,算法是离散化+线段树。这个算法的正确性是我们已经排好了序,而且每个方向的边都是成对出现的。
问题的本质是求所有的可能的哈密顿路径的情况,可以使用DFS+剪枝求解。
本问题的考点也是剪枝,有以下两种情况:
孤立区域的形成:
如图所示,孤立区域也就是被划分成了两个域的情况,这个时候无论走哪个区域另一个区域都无法访问,那么一个剪枝就可以是判断当前有无孤立区域的形成,这个产生条件比较多,有一种基本情况就是如果当前点上下(包括边界)被访问过了,而左右没被访问则必产生孤立,另一个情况则正好相反。理由是当前点上下如果被访问了,而这两个访问过的点都是由起点走过来的,也就是都连通到起点,意义就是这两个点本身是连通的。我们不能穿过被访问过的路径走交叉路线,所以说这样无论走哪里都会被包住再也出不来了。这是一个情况。
进入死胡同:
我们考虑类似围棋中的气,每个格子不需要一进一出,也就是至少有两个度,如果只有一个度则走进去后就再也出不来了,我们不必要碰到度为一的点再去回溯。而是一开始就避免度为一的点产生。剪枝方式是:除去起点和终点外如果当前点的周围未被访问的格子中有度为一的点(也就是连通到另外一个未被访问的格子),那么这个点必须要访问,理由是这个点如果被访问,就相当于他的一个入度被利用了,如果不访问则就浪费掉了。浪费之后他的度为1,以后则会只进不出。因此必须要访问,所以可以推出另一个关于度的剪枝,如果存在两个以上的度为1的点则回溯,因为不能两个同时都选,选了一个,另一个必然是死胡同。
Do not spend all your time on training or studying - this way you will probably become very exhausted and unwilling to compete more. Whatever you do - have fun. Once you find programming is no fun anymore – drop it. Play soccer, find a girlfriend, study something not related to programming, just live a life - programming contests are only programming contests, and nothing more. Don’t let them become your life - for your life is much more interesting and colorful.