1. 程式人生 > >搜尋_DFS_騎士遊歷問題

搜尋_DFS_騎士遊歷問題

問題描述:

        在國際象棋的棋盤(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;
}

執行截圖: