Đề bài:
Tít và Mít là đôi bạn tốt. Tuy nhiên, gần đây họ có cãi cọ nhau trong khi chơi cờ vua. Mít muốn biết số bước đi nhỏ nhất để quân mã xuất phát từ 1 ô để đi đến 1 ô khác trên bàn cờ vua 8×8. Tít rất thông minh nên cậu ta đã nghĩ ra thuật toán và viết ra một chương trình để giải quyết bài toán này. Tít đố Mít có thể làm được như vậy.
Tuy nhiên, Mít rất yếu về lập trình nên mong muốn bạn giúp cậu ấy. Quân mã di chuyển theo hình chữ L, chắc bạn cũng biết nên mình không giải thích nhiều nữa. Quân mã phải di chuyển theo cách trên và không được đi ra khỏi bàn cờ. Hình sau minh hoạ bàn cờ, và cách di chuyển của con mã:
Đầu vào
Có tổng số T test cases. Với mỗi test case, sẽ gồm 2 thành phần. Thành phần thứ nhất là điểm xuất phát của con mã. Và thành phần thứ hai là đích đến của con mã. Mỗi thành phần bao gồm 2 kí tự. Kí tự thứ nhất thuộc 8 kí tự từ 'a' đến 'h'. Kí tự thứ hai thuộc 8 kí tự từ '1' đến '8'.
Đầu ra
In ra số bước tối thiểu mà quân mã cần để đi từ điểm xuất phát đến đích ở các dòng riêng biệt.
Ràng buộc:
1 <= T <= 4096
Ví dụ
Đầu vào:
3
a1 h8
a1 c2
h8 c3
Đầu ra:
6
1
4
Bạn có thể tham khảo đề bài tiếng anh và submit code tại: http://www.spoj.com/problems/NAKANJ/
Phân tích
Đây là bài toán tìm đường đi ngắn nhất từ một điểm tới một điểm khác.
Do đó ta sẽ triển khai thuật toán tìm kiếm theo chiều rộng - BFS từ điểm ban đầu. Và lan sang các điểm kề với nó theo 8 hướng mà con mã có thể đi.
Lời giải
Bạn nên tự mình nghĩ ra thuật toán của bài toán trước khi tham khảo code của mình nhé. Hãy phát huy tối đa khả năng sáng tạo của bản thân. Hơn nữa code mình viết ra cũng chưa thật sự tối ưu. Nên rất mong nhận được sự chia sẻ của bạn.
Code C/C++
#include <iostream>
using namespace std;
const int MAX = 100;
int SR, SC, ER, EC; // Lưu toạ độ hàng, cột của điểm bắt đầu và kết thúc
int Mark[8][8]; // Đánh dấu vị trí con mã
typedef struct Point
{
int row, col;
Point(){};
Point(int r, int c): row(r), col(c){};
}Point;
// Hàng đợi
Point queue[MAX];
// Lần lượt là thông tin và điểm đầu, điểm cuối và độ dài của hàng đợi
int fr, re, leng;
// Mô tả đường đi của con mã
int drow[] = {-2,-1,1,2, 2, 1,-1,-2};
int dcol[] = { 1, 2,2,1,-1,-2,-2,-1};
// Thao tác Enqueue, và Dequeue với hàng đợi vòng
void EnQueue(Point p)
{
queue[re] = p;
re = (re + 1) % MAX;
leng++;
}
Point DeQueue()
{
Point p = queue[fr];
fr = (fr + 1) % MAX;
leng--;
return p;
}
void BFS()
{
fr = re = leng = 0;
EnQueue(Point(SR, SC));
Mark[SR][SC] = 0;
while(leng > 0)
{
Point p = DeQueue();
// Nếu gặp đích rồi thì dừng lại
if(p.row == ER && p.col == EC) break;
// Kiểm tra 8 hướng đi có thể của con mã
for(int i = 0; i < 8; i++)
{
int r = p.row + drow[i];
int c = p.col + dcol[i];
if(r >= 0 && r < 8 && c >= 0 && c < 8 && Mark[r][c] == -1)
{
Mark[r][c] = Mark[p.row][p.col] + 1;
EnQueue(Point(r, c));
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
// Comment dòng này trước khi submit
freopen("input.txt","r",stdin);
int T;
cin >> T;
for(int tc = 0; tc < T; tc++)
{
char temp[3];
// Nhập đầu vào và chuyển sang kiểu int
cin >> temp;
SR = temp[0] - 'a';
SC = temp[1] - '0' - 1;
cin >> temp;
ER = temp[0] - 'a';
EC = temp[1] - '0' - 1;
for(int i = 0; i < 8; i++)
for(int j = 0; j < 8; j++)
Mark[i][j] = -1;
// Triển khai thuật toán BFS để tìm số bước nhỏ nhất
// từ vị trí ban đầu đến các vị trí còn lại
BFS();
// In kết quả
cout << Mark[ER][EC] << endl;
}
return 0;
}
★ Nếu bạn thấy bài viết này hay thì hãy theo dõi mình trên Facebook để nhận được thông báo khi có bài viết mới nhất nhé:
- Facebook Fanpage: Ôn luyện thuật toán
- Facebook Group: Hỏi đáp thuật toán VN