簡單的看,圖的路徑算法可以分兩類:
- 可達性尋問題:找到一條滿足某種條件的路徑,如圖的連通性問題(簡單路徑算法),歐拉路徑,漢密頓路徑等等;
- 含權圖的最優化問題:如點對間的最短路徑,歐幾里德網;
而一切和圖有關的算法,幾乎都是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;
-
}