搜尋_DFS_騎士遊歷問題
阿新 • • 發佈:2018-11-29
問題描述:
在國際象棋的棋盤(8行×8列)上放置一個馬,按照“馬走日字”的規則,馬要遍歷棋盤,即到達棋盤上的每一格,並且每格只到達一次。若給定起始位置(x0,y0),程式設計探索出一條路徑,沿著這條路徑馬能遍歷棋盤上的所有單元格。
改進的設計思想:如果在每步選擇方向時,不是任意選擇一個方向,而是經過一定的測試和計算,“預見”每條路的“寬窄”,再選擇一條最“窄”的路先走,成功的可能性較大。理由是先走“困難的路”,光明大道留在後面。因為每一格遲早都要走到,與其把困難留在後面,不如先走“困難的路”,這樣路就會越走越寬,成功的機會就越大。這種方法稱為預見演算法. 為每個方向測定一個值――可通路數,它表示該位置上還有多少條通路。在每一格上對8個方向都進行試探,並分析比較,下一步應該選擇可通路數值最小的方向走。
下面給出基於上述設計思想的程式碼:
//騎士遊歷問題 #include <iostream> #include <cstring> #include <vector> #include <algorithm> #define mp make_pair #define fi first #define se second using namespace std; const int MAX = 100, INF = 0x3f3f3f3f; const int dx[8] = {-1, -2, -2, -1, 1, 2, 2, 1}, dy[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; typedef pair<int, int> pii; bool vis[MAX][MAX], ok; vector<pii> path; int n, parr[MAX][MAX]; void dfs(){ if(path.size() == n * n){ ok = true; for(int i = 0; i < path.size(); ++i) parr[path[i].fi][path[i].se] = i + 1; return; } int x = path.back().fi, y = path.back().se; pii dir[8]; memset(dir, 0, sizeof(dir)); //將x, y周圍的所有結點按可選方案數遞增依次加入dir中 for(int i = 0; i < 8; ++i){ dir[i].se = i; int a = x + dx[i], b = y + dy[i]; if(a >= 1 && a <= n && b >= 1 && b <= n && !vis[a][b]) for(int j = 0; j < 8; ++j){ int c = a + dx[j], d = b + dy[j]; if(c >= 1 && c <= n && d >= 1 && d <= n && !vis[c][d]) ++dir[i].fi; } } sort(dir, dir + 8); for(int i = 0; i < 8; ++i){ if(!dir[i].fi && path.size() != n * n - 1) continue; int a = x + dx[dir[i].se], b = y + dy[dir[i].se]; if(a >= 1 && a <= n && b >= 1 && b <= n && !vis[a][b]){ path.push_back(mp(a, b)), vis[a][b] = true, dfs(); if(ok) return; path.pop_back(), vis[a][b] = false; } } } int main(){ int x, y; n = 8, cin >> x >> y, vis[x][y] = true, path.push_back(mp(x, y)), dfs(); for(int i = 1; i <= n; ++i, cout << endl) for(int j = 1; j <= n; ++j) cout.width(4), cout << parr[i][j]; return 0; }
執行截圖: