POJ 3083 Children of the Candy Corn

题意

对于一个入口和出口都在边界的迷宫,有两种一定能保证走出迷宫的方案,想象你自己站在迷宫的入口,你可以一直让左手扶着墙,贴着墙走,这样最后一定会走到终点。你也可以一直让右手扶着墙,贴着墙走,最终也一定会到达终点。现在要你模拟这两种策略,分别输出这样走要经历的步数,同时再算出不限于上述策略的走出迷宫的最短路方案。

思路

对于最短路,只要使用常规的BFS就可以解决。难点在于如何模拟左手扶墙和右手扶墙。如果是以左手扶墙来遍历迷宫的话,其实是相当于每一次都先尝试去走当前面朝方向的左边,若无法走,尝试直行,还不行的话,再尝试右行和上行。每一次行走,你要想想自己身处迷宫之中,并且根据你的行走动作来改变自己的朝向,模拟这个过程就是本题的难点。

有一种比较聪明的实现方式,就是把原来的行进向量(dir),也当成朝向向量来用,往左行走相当于朝向向量左转,直行相当于无旋转,右行相当于向右旋转,下行相当于两次右旋。每一次前进时把旋转完的朝向向量当行进向量用就可以了,下一次还是按照上面所述的循环操作。

本题还有一个技巧,你只需要实现扶一边墙行走,因为从起点扶一边墙走到终点相当于从终点扶另一边墙走到起点,所以你在第一次行走完之后,只要交换起点终点的’S’和’E’,然后再执行一次从”原终点”到”原起点”的行走,就可以了。

代码

 

POJ 2386 Lake Counting / HDU 1241 Oil Deposits

题意

在一片空地上,出现了若干水洼,对于一个有水的格子,他的八连通格子(上下左右左上左下右上右下)都可以看成和他连通形成一个大水洼。现在给你空地上水的配布,你要数出有多少个水洼。

思路

典型的“水洼问题”,利用DFS解决连通性问题是十分方便的。本题只要能够正确实现DFS就可以通过了,实现DFS的要点主要是正确地使用标记和正确的递归写法。对于DFS的实作原理不太理解的话请先思考树的先序遍历,可以参考我写的这篇文章

代码

 

POJ 2251 Dungeon Master

题意

给出一个三维迷宫,上下左右前后六联通的走法,给你起点和终点,要你求从起点到终点的最少步数。

思路

拿到问题,首先发现是在一个迷宫里面进行,联通方向有六个,求最短路,且数据规模较小,这些都是适用于BFS解题的特征。接下来的关键就是正确地实现BFS了,对BFS的实作原理不太理解的话,不妨先思考在一棵树上遍历时的BFS的实作过程。在BFS的实现过程中,需要正确地判断某个点是不是有效,需要在正确的时机打访问标记,需要正确地操作队列,注意这三点一般不会产生问题。本题并不需要我们实现图结构,因为迷宫本身就已经可以看成图了。在BFS过程中,因为搜索完当前深度的全部状态,才会取出更大深度的状态,所以在BFS过程中,只要我们进入了表示走到终点的状态,就无需再考虑其他状态了,因为即使其他状态也能表示到达终点,其深度(步数)也一定不会比第一个到达的状态少,这样可以节省一些时间。还有一些诸如“连通向量”等小技巧,可以减轻编码负担。

代码

 

POJ 1469 COURSES

题意

判定给出的二分图是否存在完备匹配。

思路

按照题目要求直接建立二分路,之后运行最大匹配算法,出答案。

代码

使用了匈牙利算法。

 

POJ 1325 Machine Schedule

题意

给你两台机器和k个工件,要你设计最优调度方案。对于每个机器,一共有0 ~ n-1(或者是m-1)这n(或者是m)个状态,初始状态都是0。对于每个工件,要么在x号状态下加工,要么在y号状态下加工。你要给出两台机器一共最少要切换多少次状态,才可以加工完全部工件。

思路

这个题对于建图是很讲究的,第一眼看上去很有二分图的意思。但是如果你这么建图:一边是0~max(m,n)这些状态(因为无论是两台还是一台,其实都是一样的,因为机器运转的时间是无限的,理论上讲就算只给一个机器,也能完成任务,而且和两台机器所需要的切换次数一样),另一边是0~k这些工件,每一个工件都和他可以被加工的状态连上一条边。

但是这么建图并不能解决问题,首先,只要工件可以在0状态加工,就可以看成不存在了,因为机器无需切换状态就可以加工好,这样,我们在图中所有与0状态相连的点,和0状态本身都要去掉。其次,现在我们规约好的问题已经变成了:在一边的图中选出尽可能少的点,使得这些点的所连接的点可以覆盖对面全部的点。这是典型的“最小支配集”问题。最小支配集问题目前还是NP困难问题。所以此路已经不通了。

我们只好换一种思路,我们发现,如果把一个工件可以加工的两个状态连接起来,建立二分图之后。在这个图上,图的两边分别是A机器和B机器,图上的每一条边代表一个工件,我们要求的东西就变成了选出最少的点,使得所引出的边的集合中包含了全部的边,也就是“最小点覆盖问题”。二分图的最大匹配数等于最小覆盖数。问题可做了。

代码

使用了匈牙利算法。

 

POJ 2594 Treasure Exploration

题意

给出一个DAG(有向,没有环的图),现在要你选出尽可能少的几条路径,使得只要把你选出来的路径都走了一遍,就可以保证你已经走过的图中的每一个节点。注意,路径与路径之间,可以有公共点(即允许路径相交)。

思路

典型的“最小路径覆盖”问题。在这里直接给出此类问题的解法:

有向无环图最小不相交路径覆盖

       定义:用最少的不相交路径覆盖所有顶点。

       定理:把原图中的每个点V拆成Vx和Vy,如果有一条有向边A->B,那么就加边Ax-By。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。

       简单证明:一开始每个点都独立的为一条路径,总共有n条不相交路径。我们每次在二分图里加一条边就相当于把两条路径合成了一条路径,因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。所以有:最小路径覆盖=原图的节点数-新图最大匹配。

 

有向无环图最小可相交路径覆盖

       定义:用最小的可相交路径覆盖所有顶点。

       算法:先用floyd求出原图的传递闭包,即如果a到b有路,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。

 

所谓传递闭包(Transitive Closure)就是指把原图里面所有能直接间接联通的点之间都给连上边(如果有联通的方向限制,这个方向也要保留)之后形成的图,也就是说只要在传递闭包中存在A->B,就一定可以在原图中找到一条从A到B的路径。求传递闭包使用Warshall算法,最简单的实现就是借助邻接矩阵了(下图直接截自离散数学课件):

QQ20150829-1@2x

代码

以下实现使用了Hopcraft-Karp算法和Warshall算法。

 

POJ 2446 Chessboard

题意

给出一个M*N大小的棋盘(M,N都非常小),棋盘上戳了几个洞,然后问你有没有可能用若干张1*2的骨牌,横着或者竖着填满整个棋盘,有洞的地方是不可以放置骨牌的。

思路

因为骨牌是1*2的,如果我们把整个棋盘看成一个图,每个没洞的地方都是一个节点,在棋盘上只要相邻(上下左右四联通)就可以看成两个节点有一条边相连,这样我们就可以发现,每一块骨牌正好就是图上的一条边。所以我们就把问题转化为了:给出一个图,要你用选出尽可能少的边,使得图上的每一个顶点都是选出的边中的某一条的端末,也就是“最小边覆盖”问题。

最小边覆盖=图中点的个数-最大匹配数=最大独立集

所以接下来的事情就很简单,按着上面的说法建图,然后运行一次最大匹配算法,之后作个差就搞定了。

还要注意一点,本题数据给出的形式可能和预想的不一样,挖洞的点是横坐标在前,纵坐标在后,如果WA,不妨检查一下是不是输入的时候做反了。

代码

本实现使用了匈牙利算法。

 

POJ 1274 The Perfect Stall

题意

求二分图最大匹配。

思路

教学题,只要使用了二分图/网络流就可以通过。在这里我强行使用了Hopcroft-Karp Algorithm,因为看了很长时间自己也很难理解,决定抄一遍别人的代码仔细领悟。核心部分我加了好多注释帮助理解。

代码

 

POJ 3090 Visible Lattice Points

题意

给出了一个1001×1001的点阵,和查询q,你要求出从(0,0)点到(q,q)点有多少种不同的点到原点的斜率(也就是题目中所说的“可见”),原点到原点不算。

思路

很直接的就可以想到算出所有的点到原点的斜率,预处理出答案,我们的做法是:从原点开始,一圈一圈地向外扩张,对于每个点都计算一边斜率,然后确认该斜率是否出现过,没出现过的话,当前圈上的点到原点的不同的斜率个数+1,这样求出每一圈的数目,最后我们再求一个前缀和就好了。

时间很理想,是O(n^2*log(n))的,然而POJ上超时了。我拿到ideone上测速,是700ms,可以过的,可能是评测机不在状态吧ˊ_>ˋ。补救的方法就是打了个表,反正数据也很少。

然而这个题的精髓并不是这么水过去的,这个题其实是一个包装过了的法雷级数(Wikipedia::法里数列 可能需要科学上网),如果试着一圈圈地写一下斜率,就会发现每一圈正好满足分子和分母互质的特性,每一圈的个数其实是欧拉函数值,整个答案是欧拉函数前缀和。所以我们就有了O(n),的处理方法,详细的可以看这个题POJ 2478 Farey Sequence

明明是刚做过的题居然都没有反应过来看来我真的是太迟钝了呢。

代码(暴力解法)

 

POJ 2478 Farey Sequence

题意

介绍性的问题,法雷级数(Wikipedia::法里數列 可能需要科学上网)

简单暴力,给出一个n,求出对于1~n中每一个数,有多少个小于等于他的数与他互质,把这些数量加在一起是多少,其实就是求欧拉函数的前缀和。

思路

欧拉函数有两种得到的方法,一是使用分解因子的方法,得到单个数的欧拉函数值,时间复杂度O(sqrt(n) * log(n))。如下:

可以发现这样算的话如果只求一个数的欧拉函数还是不亏的,但是如果要求非常非常多的欧拉函数,时间就会爆炸,所以我们不可以在这个题用这种方法。

第二种方法是利用欧拉函数的若干性质: 欧拉函数线性筛法详解(Lytning),然后把素数的线性筛法和求欧拉函数结合到一起,如果要求非常多的欧拉函数,使用这种方法预处理是非常快的,而且还顺便求了素数,一石二鸟。

快速地得到欧拉函数值之后,前缀和什么的就是很简单的事情了。

代码

 

Scroll to top