简单的看,图的路径算法可以分两类:
- 可达性寻问题:找到一条满足某种条件的路径,如图的连通性问题(简单路径算法),欧拉路径,汉密顿路径等等;
- 含权图的最优化问题:如点对间的最短路径,欧几里德网;
而一切和图有关的算法,几乎都是DFS和BFS,这两家伙乍一看浓眉大眼的。他们所反映的,是计算机中的逻辑性与次序性,世界纷繁复杂,DFS与BFS是探索这些杂乱无章的一种逻辑很清晰、次序性很明显的思维方式。我当时学习图的时候,第一感觉就是乱糟糟恶心的一拖东西堆着,哪像树呢?多好看?为什么好看呢?因为好理解,不断的分叉下去,是吧,规律性很明显!在Wikipedia上找到了两张图,可以很好的说明思维的次序性问题:
好了,回到fence问题上来,欧拉路径的存在性问题已经有了结论了,请见Robert Sedgewick的《Algorithms in C++》,结论是这样的:
- 对于一个无向图,如果它每个点的度都是偶数,那么它存在一条欧拉回路;
- 如果有且仅有2个点的度为奇数,那么它存在一条欧拉路;
- 如果超过2个点的度为奇数,那么它就不存在欧拉路了。
找出欧拉路的方法就是采用DFS的方式,对于当前的点,把所有点从小到大的搜索,找到和它相连的,找到一个之后删除它们之间的连线,并去搜索新的那个点,如果没有找到点和它相连,那么就把这个点加入输出队列。
以上就是这个题的主题思路了,但是仅仅这么做会超时的。前面说:“世界纷繁复杂,DFS与BFS是探索这些杂乱无章的一种逻辑很清晰、次序性很明显的思维方式”,现在,有了这个思维的主线条了,接下来就是挖掘其中的细节,也就是剪枝。太多的剪枝我也没发现很多,不过有一点是,前面我们说,“找到和它相连的,找到一个之后删除它们之间的连线”,这里是会产生一个问题的,那就是有可能删除了一些要命的边,使得图不连通了:
像上面的,中间那个黄点点挂了,要是左或右还没走完,整个图怎么都走不出一条欧拉回路了。当然图画的有点丑,呵呵~
所以这里又有些考察基础了。Robert的书里有过几个概念:
割点:若一个点删除后(也就是与之相连的边统统去掉),无向图不再连通,那么此点称为割点。
桥:若一条边断去后,无向图不再连通,那么此边称为桥。桥有一个很好的性质,就是DFS一个无向图,那么这个过程必定要经过桥。
块:没有割点的无向图称为2-连通分支,也称作块。
现在应该比较清楚了,也就是说,在进行DFS搜索前,可以先用FloodFill灌水算法判断一下图的连通性,FloodFill算是OIer菜谱上的算法了吧,所以还是基础啊。。。
BTW,FloodFill不一定就是递归的,下面的代码中用的就是一个循环。
-
/*
-
ID:fairyroad
-
LANG:C++
-
TASK:fence
-
*/
-
#include <fstream>
-
#include <vector>
-
#include <cstring>
-
using namespace std;
-
-
ifstream fin("fence.in");
-
ofstream fout("fence.out");
-
-
const int MAX = 505;
-
int edge[MAX][MAX];
-
int nodeCnt[MAX];
-
bool visited[MAX];
-
int F;
-
-
vector<int> ans;
-
-
bool floodFill(int node)
-
{
-
memset(visited, 0, sizeof(visited));
-
visited[node] = true;
-
-
vector<int> Q;
-
Q.push_back(node);
-
-
while(!Q.empty())
-
{
-
node = Q.back();
-
Q.pop_back();
-
for (int i = 0; i < MAX; ++i)
-
{
-
if(edge[node][i] && !visited[i])
-
{
-
Q.push_back(i);
-
visited[i] = true;
-
}
-
}
-
}
-
-
for (int i = 1; i < MAX; ++i)
-
{
-
if (!visited[i] && nodeCnt[i])
-
{
-
return false;
-
}
-
}
-
-
return true;
-
}
-
-
bool dfs(int curr)
-
{
-
if((int)ans.size() == F+1)
-
{
-
for(size_t i = 0; i < ans.size(); ++i)
-
fout << ans[i] << endl;
-
//exit(0);
-
return true;
-
}
-
-
if(!floodFill(curr))
-
return false;
-
-
for(int i = 1; i < MAX; ++i)
-
{
-
if(edge[curr][i])
-
{
-
ans.push_back(i);
-
--edge[curr][i]; --edge[i][curr];
-
--nodeCnt[curr]; --nodeCnt[i];
-
-
if(dfs(i))
-
return true;
-
-
ans.pop_back();
-
++edge[curr][i]; ++edge[i][curr];
-
++nodeCnt[curr]; ++nodeCnt[i];
-
}
-
}
-
-
return false;
-
}
-
-
int main()
-
{
-
fin >> F;
-
int i, v1, v2;
-
for(i = 0; i < F; ++i)
-
{
-
fin >> v1 >> v2;
-
++edge[v1][v2]; ++edge[v2][v1];
-
++nodeCnt[v1]; ++nodeCnt[v2];
-
}
-
-
int start = -1, oddNode = 0;
-
for(int i = 0; i < MAX; ++i)
-
{
-
if(nodeCnt[i] & 0x1u)
-
{
-
++oddNode;
-
if(start == -1) start = i;
-
}
-
}
-
-
if(oddNode != 2) // 题目保证了有解
-
start = 1;
-
-
ans.push_back(start);
-
dfs(start);
-
-
return 0;
-
}