Con đường ngắn nhất của Knight trên bàn cờ


95

Tôi đang luyện tập cho một cuộc thi lập trình sắp tới và tôi đã tình cờ gặp một câu hỏi mà tôi hoàn toàn hoang mang. Tuy nhiên, tôi cảm thấy đó là một khái niệm mà tôi nên học ngay bây giờ hơn là khoanh tay mà nó không bao giờ xuất hiện.

Về cơ bản, nó giải quyết một quân cờ trên bàn cờ. Bạn được cung cấp hai đầu vào: vị trí bắt đầu và vị trí kết thúc. Mục đích là sau đó tính toán và in ra con đường ngắn nhất mà kỵ sĩ có thể đi để đến vị trí mục tiêu.

Tôi chưa bao giờ giải quyết những thứ khó khăn ngắn nhất, và tôi thậm chí không biết bắt đầu từ đâu. Tôi sử dụng logic nào để giải quyết vấn đề này?

Tái bút: Nếu nó có liên quan, họ muốn bạn bổ sung các bước di chuyển bình thường của hiệp sĩ bằng cách cho phép nó di chuyển đến bốn góc của hình vuông được tạo thành bởi (có khả năng) tám nước đi mà một hiệp sĩ có thể thực hiện, cho rằng tâm của hình vuông là vị trí của hiệp sĩ.


Bạn có thể làm rõ PS? Ý bạn là, nếu một kỵ sĩ đang ở E4, nó có thể di chuyển đến C2, C6, G2 và G6?
Steve Tjoa

Vâng, ngoài việc di chuyển bình thường.
Kyle Hughes

1
Dưới đây là một số phân tích toán học của vấn đề: math.stackexchange.com/questions/104700/…
Graeme Pyle

Câu trả lời:


28

Bạn có một biểu đồ ở đây, trong đó tất cả các nước đi có sẵn được kết nối với nhau (giá trị = 1) và các nước đi không khả dụng bị ngắt kết nối (giá trị = 0), ma trận thưa thớt sẽ giống như sau:

(a1,b3)=1,
(a1,c2)=1,
  .....

Và đường đi ngắn nhất của hai điểm trong biểu đồ có thể được tìm thấy bằng cách sử dụng http://en.wikipedia.org/wiki/Dijkstra's_algorithm

Mã giả từ trang wikipedia:

function Dijkstra(Graph, source):
   for each vertex v in Graph:           // Initializations
       dist[v] := infinity               // Unknown distance function from source to v
       previous[v] := undefined          // Previous node in optimal path from source
   dist[source] := 0                     // Distance from source to source
   Q := the set of all nodes in Graph
   // All nodes in the graph are unoptimized - thus are in Q
   while Q is not empty:                 // The main loop
       u := vertex in Q with smallest dist[]
       if dist[u] = infinity:
          break                         // all remaining vertices are inaccessible from source
       remove u from Q
       for each neighbor v of u:         // where v has not yet been removed from Q.
           alt := dist[u] + dist_between(u, v) 
           if alt < dist[v]:             // Relax (u,v,a)
               dist[v] := alt
               previous[v] := u
   return dist[]

BIÊN TẬP:

  1. như kẻ ngu ngốc, đã nói bằng cách sử dụng http://en.wikipedia.org/wiki/A*_algorithm có thể nhanh hơn.
  2. cách nhanh nhất là tính toán trước tất cả các khoảng cách và lưu nó vào ma trận đầy đủ 8x8. tốt, tôi sẽ gọi đó là gian lận, và chỉ hoạt động vì vấn đề nhỏ. Nhưng đôi khi các cuộc thi sẽ kiểm tra tốc độ chạy chương trình của bạn.
  3. Điểm chính là nếu bạn đang chuẩn bị cho một cuộc thi lập trình, bạn phải biết các thuật toán phổ biến bao gồm cả Dijkstra. Điểm khởi đầu tốt là đọc Introduction to AlgorithmsISBN 0-262-03384-4. Hoặc bạn có thể thử wikipedia, http://en.wikipedia.org/wiki/List_of_algorithm

Điều này có vẻ phức tạp so với giải pháp của Mustafa dưới đây.
lpapp

Ý bạn là gì khi di chuyển không có sẵn? Một hiệp sĩ có thể đến bất kỳ quảng trường nào đúng không !?
everlasto

51

CHỈNH SỬA: Xem câu trả lời của Simon , nơi anh ấy đã sửa công thức được trình bày ở đây.

Trên thực tế, có một công thức O (1)

Đây là hình ảnh mà tôi đã thực hiện để hình dung nó (Các hình vuông mà một hiệp sĩ có thể đạt được khi di chuyển thứ N được sơn cùng màu). Knight's Move

Bạn có thể nhận thấy mô hình ở đây?

Mặc dù chúng ta có thể nhìn thấy mô hình, nhưng thực sự rất khó để tìm ra hàm f( x , y )trả về số lần di chuyển cần thiết để đi từ hình vuông ( 0 , 0 )sang hình vuông( x , y )

Nhưng đây là công thức hoạt động khi 0 <= y <= x

int f( int x , int y )
{
    int delta = x - y;

    if( y > delta )
        return 2 * ( ( y - delta ) / 3 ) + delta;
    else
        return delta - 2 * ( ( delta - y ) / 4 );
}

Lưu ý: Câu hỏi này đã được hỏi vào SACO 2007 Ngày 1
Và các giải pháp ở đây


8
Bất kỳ cơ hội nào bạn có thể mô tả cách bạn làm ra công thức đó?
kybernetikos

3
Mã này có hoạt động không? Nếu một hiệp sĩ đang ở vị trí (0,0) và tôi muốn di chuyển nó đến điểm (1,0). Điều này thỏa mãn 0 <= y <= x. delta = 1-0 = 1. y không lớn hơn delta (0 <1). Điều này có nghĩa là tôi sẽ cho trường hợp khác. delta - 2 * ((delta - y) / 4) = 1-2 ((1-0) / 4) = 1-1 / 2 = 1. Không có nghĩa là tôi có thể di chuyển hiệp sĩ từ (0,0) đến (1,0) trong một lần di chuyển. Câu hỏi đặt ra là thuật ngữ này có hoạt động không? Hay tôi đang làm gì sai?
SimpleApp

3
Có vẻ như nó chỉ hoạt động cho các vị trí có thể trực tiếp. Nhưng nếu người dùng cung cấp (2,2) thì trả về 0 và nếu người dùng cung cấp (4,4) thì trả về 2 là sai.
yunas

6
Nó nên được 2 * floor((y - delta) / 3) + deltadelta - 2 * floor((delta - y) / 4). Đây là giải pháp chính thức từ trang cuộc thi này, nhưng nó sai. Phương trình đầu tiên này (từ if) trả về các câu trả lời sai. Trên bàn cờ [-1000..1000] x [-1000..1000], có kích thước lớn 2001x2001 (nhưng không giới hạn về mặt logic), câu trả lời đã cho là 2.669.329 trong số 4.004.001 trường đúng (66,66%). Bất cứ ai biết giải pháp làm việc mà không có bất kỳ vòng lặp nào?
Robo Robok

2
Tôi đồng ý giải pháp này không hoạt động. Xem các câu trả lời khác như stackoverflow.com/a/26888893/4288232 để biết giải pháp O (1) đang hoạt động.
TimSC

45

Đây là một giải pháp O (1) chính xác, nhưng đối với trường hợp quân sĩ chỉ di chuyển như một hiệp sĩ cờ vua và trên một bàn cờ vô hạn:

https://jsfiddle.net/graemian/5qgvr1ba/11/

Chìa khóa để tìm ra điều này là để ý các mẫu nổi lên khi bạn vẽ bảng. Trong biểu đồ bên dưới, số trong ô vuông là số lần di chuyển tối thiểu cần thiết để đến ô vuông đó (bạn có thể sử dụng tìm kiếm theo chiều rộng để tìm số này):

Hoa văn

Vì nghiệm đối xứng qua trục và đường chéo nên tôi chỉ rút ra được trường hợp x> = 0 và y> = x.

Khối dưới cùng bên trái là vị trí bắt đầu và các số trong các khối thể hiện số lần di chuyển tối thiểu để đến được các khối đó.

Có 3 mẫu cần lưu ý:

  • Các nhóm dọc màu xanh lam tăng dần gồm 4
  • Các đường chéo màu đỏ "chính" (chúng chạy từ trên trái sang dưới cùng bên phải, giống như dấu gạch chéo ngược)
  • Đường chéo màu xanh lá cây "phụ" (cùng hướng với màu đỏ)

(Đảm bảo rằng bạn thấy cả hai tập hợp đường chéo là từ trên trái sang dưới cùng bên phải. Chúng có số lần di chuyển không đổi. Các đường chéo dưới cùng bên trái trên cùng bên phải phức tạp hơn nhiều.)

Bạn có thể lấy công thức cho mỗi. Các khối màu vàng là trường hợp đặc biệt. Vì vậy, giải pháp trở thành:

function getMoveCountO1(x, y) {

    var newXY = simplifyBySymmetry(x, y);

    x = newXY.x;
    y = newXY.y;

    var specialMoveCount = getSpecialCaseMoveCount(x ,y);

    if (specialMoveCount !== undefined)
        return specialMoveCount;

    else if (isVerticalCase(x, y))
        return getVerticalCaseMoveCount(x ,y);

    else if (isPrimaryDiagonalCase(x, y))
        return getPrimaryDiagonalCaseMoveCount(x ,y);

    else if (isSecondaryDiagonalCase(x, y))
        return getSecondaryDiagonalCaseMoveCount(x ,y);

}

với khó nhất là các nhóm dọc:

function isVerticalCase(x, y) {

    return y >= 2 * x;

}

function getVerticalCaseMoveCount(x, y) {

    var normalizedHeight = getNormalizedHeightForVerticalGroupCase(x, y);

    var groupIndex = Math.floor( normalizedHeight / 4);

    var groupStartMoveCount = groupIndex * 2 + x;

    return groupStartMoveCount + getIndexInVerticalGroup(x, y);

}

function getIndexInVerticalGroup(x, y) {

    return getNormalizedHeightForVerticalGroupCase(x, y) % 4;

}

function getYOffsetForVerticalGroupCase(x) {

    return x * 2;

}

function getNormalizedHeightForVerticalGroupCase(x, y) {

    return y - getYOffsetForVerticalGroupCase(x);

}

Xem các câu đố cho các trường hợp khác.

Có lẽ có những mẫu đơn giản hơn hoặc trang nhã hơn mà tôi đã bỏ qua? Nếu vậy, tôi rất thích nhìn thấy chúng. Đặc biệt, tôi nhận thấy một số mẫu đường chéo trong các trường hợp dọc màu xanh lam, nhưng tôi chưa khám phá chúng. Bất kể, giải pháp này vẫn thỏa mãn ràng buộc O (1).


Điều này dường như không xử lý được các trường hợp góc (theo nghĩa đen). Nếu "0" là ô vuông phía dưới bên trái trên bàn cờ (a1), thì bạn không thể đến khoảng trống "2" gần nhất (b2) trong hai lần di chuyển. Bởi vì để làm như vậy, bước đầu tiên của bạn sẽ phải là không gian không tồn tại ở bên trái của (a3).
John Hascall

Phải, tôi đã thay đổi câu trả lời của tôi bao gồm vô hạn bàn cờ giả định
Graeme Pyle

@JonatasWalker Vui lòng giải thích, tôi không thấy có vấn đề gì khi chuyển từ (8,0) sang (0,0). Cần 4 lần di chuyển?
Graeme Pyle

Xin lỗi @GraemePyle, lỗi của tôi, xóa nhận xét của tôi.
Jonatas Walker

2
chào @GraemePyle - Tôi đồng ý với bạn đây là cách tiếp cận lập trình tổng thể tốt nhất. Nhân tiện, sơ đồ tuyệt vời!
Fattie

22

Vấn đề rất thú vị mà tôi đã gặp phải gần đây. Sau khi xem xét một số giải pháp, tôi đã cố gắng khôi phục công thức phân tích ( O(1) time and space complexity) được đưa ra trên các giải pháp Ngày 1 SACO 2007 .

Trước hết, tôi muốn đánh giá cao Graeme Pyle vì hình ảnh rất đẹp đã giúp tôi sửa công thức.

Vì lý do nào đó (có thể vì đơn giản hóa hoặc đẹp mắt hoặc chỉ là do nhầm lẫn) họ đã chuyển minusdấu hiệu thành floortoán tử, kết quả là họ đã sai công thức floor(-a) != -floor(a) for any a.

Đây là công thức phân tích chính xác:

var delta = x-y;
if (y > delta) {
    return delta - 2*Math.floor((delta-y)/3);
} else {
    return delta - 2*Math.floor((delta-y)/4);
}

Công thức hoạt động cho tất cả các cặp (x, y) (sau khi áp dụng đối xứng trục và đường chéo) ngoại trừ (1,0) và (2,2) trường hợp góc, không thỏa mãn với mẫu và được mã hóa cứng trong đoạn mã sau:

function distance(x,y){
     // axes symmetry 
     x = Math.abs(x);
     y = Math.abs(y);
     // diagonal symmetry 
     if (x < y) {
        t = x;x = y; y = t;
     }
     // 2 corner cases
     if(x==1 && y == 0){
        return 3;
     }
     if(x==2 && y == 2){
        return 4;
     }
    
    // main formula
    var delta = x-y;
		if(y>delta){
  		return delta - 2*Math.floor((delta-y)/3);
  	}
  	else{
  		return delta - 2*Math.floor((delta-y)/4);
  	}
}


$body = $("body");
var html = "";
for (var y = 20; y >= 0; y--){
	html += '<tr>';
	for (var x = 0; x <= 20; x++){
  	html += '<td style="width:20px; border: 1px solid #cecece" id="'+x+'_'+y+'">'+distance(x,y)+'</td>';
  }
  html += '</tr>';
}

html = '<table>'+html+'</table>';
$body.append(html);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Lưu ý: jQuery chỉ được sử dụng để minh họa, cho distancehàm xem mã .


2
@OlegAbrazhaev Hàm "khoảng cách" là một hàm phân tích và có thể tính số bước cho vị trí (x, y) đã cho trong thời gian O (1). Về cơ bản trong công thức này, chúng tôi không phụ thuộc vào bảng (tôi đoán bạn có nghĩa là "bảng vô hạn" đó là thuộc tính), do đó nó sẽ hoạt động.
simon

2
@simon Ai đó có thể giải thích công thức chính được không? Tôi cảm thấy khó khăn để có thể giải thích nó bằng những từ đơn giản
MarBVI

1
@MarBVI Nếu chúng ta ở gần đường y = x, chúng ta có thể giảm x + y đi 3 trong mỗi lần di chuyển bằng cách ở gần đường y = x. Nếu chúng ta ở gần dòng y = 0, chúng ta có thể giảm x đi 2 trong mỗi lần di chuyển bằng cách ở gần dòng y = 0. Đó là lý do tại sao chúng ta có 2 trường hợp, chính xác hơn ở đây tôi muốn nói gì khi nói gần dòng cụ thể: 1. Gần dòng y = x, ý tôi là phần bị giới hạn bởi dòng y = x và y = x / 2 (y> x / 2 ). 2. Gần dòng y = 0, ý tôi là phần bị giới hạn bởi y = 0 và y = x / 2 dòng (y <x / 2). Lấy tất cả những gì đã đề cập ở trên và nếu chúng ta loại bỏ Math.floor và đơn giản hóa công thức chính, chúng ta sẽ nhận được công thức sau: if (y> x / 2) then {return (x + y) / 3} else {return x / 2}
simon

1
@simon tuyệt vời, điều đó làm cho nó rõ ràng hơn ... ty cho thời gian của bạn :)
MarBVI

1
đề phòng, cuộc thi BAPC2017 cũng có một câu hỏi tên là Cuộc thi Marathon của Hiệp sĩ trên một bảng vô cực mà công thức này giải được nó một cách hoàn hảo. 2017.bapc.eu/files/preliminaries_problems.pdf
Amir-Mousavi,

19

Vâng, Dijkstra và BFS sẽ giúp bạn có câu trả lời, nhưng tôi nghĩ bối cảnh cờ vua của vấn đề này cung cấp kiến ​​thức có thể mang lại giải pháp nhanh hơn nhiều so với thuật toán đường đi ngắn nhất chung chung, đặc biệt là trên bàn cờ vô hạn.

Để đơn giản, hãy mô tả bàn cờ vua là mặt phẳng (x, y). Mục tiêu là tìm đường đi ngắn nhất từ ​​(x0, y0) đến (x1, y1) chỉ sử dụng các bước ứng viên (+ -1, + -2), (+ -2, + -1) và (+ -2 , + -2), như được mô tả trong PS của câu hỏi

Đây là quan sát mới: vẽ một hình vuông với các góc (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4) . Tập hợp này (gọi là S4) chứa 32 điểm. Con đường ngắn nhất từ ​​bất kỳ điểm nào trong số 32 điểm này đến (x, y) yêu cầu chính xác hai lần di chuyển .

Đường đi ngắn nhất từ ​​bất kỳ điểm nào trong số 24 điểm trong tập S3 (được xác định tương tự) đến (x, y) yêu cầu ít nhất hai lần di chuyển .

Do đó, nếu | x1-x0 |> 4 hoặc | y1-y0 |> 4 thì đường đi ngắn nhất từ ​​(x0, y0) đến (x1, y1) lớn hơn hai lần đường đi ngắn nhất từ ​​(x0, y0) đến S4. Và vấn đề thứ hai có thể được giải quyết nhanh chóng với sự lặp lại đơn giản.

Cho N = max (| x1-x0 |, | y1-y0 |). Nếu N> = 4, thì đường đi ngắn nhất từ ​​(x0, y0) đến (x1, y1) có bậc (N / 2) bước.


1
Có phải chỉ tôi là người đang bối rối về câu trả lời này? "vẽ một hình vuông với các góc (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4). Tập hợp này (gọi nó S4) chứa 32 điểm ”. Không, không, nó chứa 81 vì nó là một hình vuông 9x9? Ngoài ra, "Cho N = max (| x1-x0 |, | y1-y0 |). Nếu N> = 4, thì đường đi ngắn nhất từ ​​(x0, y0) đến (x1, y1) có ceil (N / 2) các bước. " Điều đó không đúng, lấy ví dụ x0 = 0, y0 = 0, x1 = 4, y1 = 4, đường đi ngắn nhất là 4, không phải 2 như công thức đó gợi ý.
satoshi

1
(1) Tập hợp chỉ đề cập đến các điểm trên ranh giới của chính hình vuông. Điều đó có 32 điểm / vị trí. (2) Khi bạn tính đến PS của người đăng bài về các nước đi bổ trợ (xem thêm các nhận xét trong bài viết gốc), số nước đi tối thiểu sẽ trở thành hai.
Steve Tjoa

Cảm ơn, điều đó có ý nghĩa ngay bây giờ :)
satoshi

điều gì sẽ xảy ra nếu một bảng là vô hạn? trong trường hợp này chỉ BFS sẽ làm việc tốt
Oleg Abrazhaev

@SteveTjoa xin lỗi, tôi không hiểu tại sao bạn lại đề cập (+ -2, + -2) di chuyển, vì điều đó là không thể đối với một hiệp sĩ
Pavel Bely

12

Câu trả lời O (1) ở trên [ https://stackoverflow.com/a/8778592/4288232 của Mustafa Serdar Şanlı] không thực sự hoạt động. (Đánh dấu (1,1) hoặc (3,2) hoặc (4,4), dành cho các trường hợp cạnh rõ ràng của (1,0) hoặc (2,2)).

Dưới đây là một giải pháp xấu hơn nhiều (python), giải pháp này hoạt động (với các "thử nghiệm" được bổ sung):

def solve(x,y):
        x = abs(x)
        y = abs(y)
        if y > x:
            temp=y
            y=x
            x=temp  
        if (x==2 and y==2):
            return 4
        if (x==1 and y==0):
            return 3

    if(y == 0 or float(y) / float(x) <= 0.5):
        xClass = x % 4
        if (xClass == 0):
            initX = x/2
        elif(xClass == 1):
            initX = 1 + (x/2)
        elif(xClass == 2):
            initX = 1 + (x/2)
        else:
            initX = 1 + ((x+1)/2)

        if (xClass > 1):
            return initX - (y%2)
        else:
            return initX + (y%2)
    else:
        diagonal = x - ((x-y)/2)
        if((x-y)%2 == 0):
            if (diagonal % 3 == 0):
                return (diagonal/3)*2
            if (diagonal % 3 == 1):
                return ((diagonal/3)*2)+2
            else:
                return ((diagonal/3)*2)+2
        else:
            return ((diagonal/3)*2)+1


def test():
    real=[
    [0,3,2,3,2,3,4,5,4,5,6,7,6,7],
    [3,2,1,2,3,4,3,4,5,6,5,6,7,8],
    [2,1,4,3,2,3,4,5,4,5,6,7,6,7],
    [3,2,3,2,3,4,3,4,5,6,5,6,7,8],
    [2,3,2,3,4,3,4,5,4,5,6,7,6,7],
    [3,4,3,4,3,4,5,4,5,6,5,6,7,8],
    [4,3,4,3,4,5,4,5,6,5,6,7,6,7],
    [5,4,5,4,5,4,5,6,5,6,7,6,7,8],
    [4,5,4,5,4,5,6,5,6,7,6,7,8,7],
    [5,6,5,6,5,6,5,6,7,6,7,8,7,8],
    [6,5,6,5,6,5,6,7,6,7,8,7,8,9],
    [7,6,7,6,7,6,7,6,7,8,7,8,9,8]]

    for x in range(12):
        for y in range(12):
            res = solve(x,y)
            if res!= real[x][y]:
                print (x, y), "failed, and returned", res, "rather than", real[x][y]
            else:
               print (x, y), "worked. Cool!"

test()

10
Đề cập đến các câu trả lời là abovehoặc belowkhông thực sự hoạt động trên SO.
Petr Peller

1
Đây là phiên bản của tôi trong python 2/3. Tôi đã cố gắng đơn giản hóa hàm giải quyết nhưng nó không dễ dàng! gist.github.com/TimSC/8b9a80033f3a22a708a4b9741931c591
TimSC

9

Những gì bạn cần làm là nghĩ về các nước đi có thể có của quân mã dưới dạng một đồ thị, trong đó mọi vị trí trên bàn cờ là một nút và các nước có thể di chuyển sang vị trí khác là một cạnh. Không cần đến thuật toán dijkstra, bởi vì mọi cạnh đều có trọng số hoặc khoảng cách như nhau (chúng đều dễ hoặc ngắn để thực hiện). Bạn chỉ có thể thực hiện tìm kiếm BFS từ điểm bắt đầu cho đến khi bạn đến vị trí cuối cùng.


1
+ !, đối với vấn đề cụ thể này BFS là đủ.
TiansHUo

3
BFS có thể là đủ, nhưng một BST đơn giản sẽ làm nổ tung nhiều truy vấn - bạn sẽ cần lưu vào bộ nhớ cache những ô vuông nào bạn đã truy cập. Và sau đó BFS bắt đầu trông hơi giống thuật toán Dijkstra ...
Charles Stewart

Cách tốt nhất để theo dõi tất cả các vị trí mà chúng ta đã đi để cây BFS chỉ phát triển về phía trước và khi chúng ta phát hiện ra các nút có thể sử dụng được từ một điểm mới, chúng ta sẽ không thêm lại nút cũ nữa..và bị mắc kẹt trong một vòng lặp vô hạn!
Nitish Upreti

Tôi ở đây cho rằng chúng ta có thể làm gì bằng cách giữ lại vị trí hiệp sĩ cuối cùng của mình?
Nitish Upreti

7

Giải pháp từ các nguyên tắc đầu tiên trong Python

Lần đầu tiên tôi gặp sự cố này trong một bài kiểm tra Codility. Họ đã cho tôi 30 phút để giải nó - tôi mất nhiều thời gian hơn thế để đạt được kết quả này! Vấn đề là: cần bao nhiêu nước đi để một hiệp sĩ đi từ 0,0 đến x, y chỉ sử dụng các nước đi của Hiệp sĩ hợp pháp. x và y ít nhiều không bị ràng buộc (vì vậy chúng ta không nói ở đây về một bàn cờ 8x8 đơn giản).

Họ muốn một giải pháp O (1). Tôi muốn một giải pháp mà chương trình đang giải quyết vấn đề một cách rõ ràng (tức là tôi muốn một cái gì đó rõ ràng đúng hơn mẫu của Graeme - các mẫu có thói quen chia nhỏ nơi bạn không nhìn thấy) và tôi thực sự muốn không phải dựa vào công thức không tồn tại, như trong giải pháp của Mustafa

Vì vậy, đây là giải pháp của tôi, cho những gì nó đáng giá. Bắt đầu, như những người khác đã làm, bằng cách lưu ý rằng lời giải là đối xứng về các trục và đường chéo, vì vậy chúng ta chỉ cần giải cho 0> = y> = x. Để đơn giản hóa việc giải thích (và mã), tôi sẽ đảo ngược vấn đề: kỵ sĩ bắt đầu từ x, y và đang nhắm tới 0,0.

Giả sử chúng ta thu nhỏ vấn đề xuống vùng lân cận của điểm gốc. Chúng ta sẽ tìm hiểu ý nghĩa thực sự của 'vicinty', nhưng hiện tại, chúng ta hãy viết một số giải pháp trong một bảng cheatsheet (nguồn gốc ở phía dưới bên trái):

2 1 4 3
3 2 1 2
0 3 2 3

Vì vậy, với x, y trên lưới, chúng ta chỉ có thể đọc số lần di chuyển đến điểm gốc.

Nếu chúng ta đã bắt đầu bên ngoài lưới, chúng ta phải quay lại với nó. Chúng tôi giới thiệu 'đường giữa', là đường được biểu diễn bởi y = x / 2. Bất kỳ quân nào tại x, y trên đường đó đều có thể quay trở lại bảng gian lận bằng cách sử dụng một loạt các bước di chuyển 8 giờ (nghĩa là: (-2, -1) di chuyển). Nếu x, y nằm trên đường giữa, thì chúng ta sẽ cần di chuyển 8 giờ và 7 giờ liên tiếp, và nếu nó nằm dưới đường giữa, chúng ta sẽ cần liên tiếp 8 giờ và 10 giờ 'đồng hồ di chuyển. Hai điều cần lưu ý ở đây:

  • Các chuỗi này là những con đường ngắn nhất. (Muốn tôi chứng minh điều đó hay là điều hiển nhiên?)
  • Chúng tôi chỉ quan tâm đến số lần di chuyển như vậy. Chúng ta có thể kết hợp các bước di chuyển theo bất kỳ thứ tự nào.

Vì vậy, chúng ta hãy xem xét các bước di chuyển ở đường giữa. Những gì chúng tôi đang tuyên bố là:

  • (dx; dy) = (2,1; 1,2) (n8; n7) (ký hiệu ma trận, không có sắp chữ toán học - vectơ cột (dx; dy) bằng ma trận vuông nhân với vectơ cột (n8; n7) - the số 8 giờ di chuyển và số 7 giờ di chuyển), và tương tự;

  • (dx; dy) = (2,2; 1, -1) (n8; n10)

Tôi khẳng định rằng dx, dy sẽ là khoảng (x, y), vì vậy (x-dx, y-dy) sẽ nằm trong vùng lân cận của điểm gốc (bất kỳ 'vùng lân cận' nào hóa ra).

Hai dòng trong mã tính toán các thuật ngữ này là giải pháp cho những điều này, nhưng chúng được chọn để có một số thuộc tính hữu ích:

  • Công thức đường giữa ở trên di chuyển (x, y) đến một trong (0,0), (1,1) hoặc (2,2).
  • Công thức đường giữa di chuyển (x, y) đến một trong (0,0), (1,0), (2,0) hoặc (1,1).

(Bạn có muốn bằng chứng về những điều này không?) Vì vậy, khoảng cách của Hiệp sĩ sẽ là tổng của n7, n8, n10 và cheatsheet [x-dx, y-dy], và cheatsheet của chúng tôi giảm xuống thành:

. . 4
. 2 .
0 3 2

Bây giờ, đây chưa phải là phần cuối của câu chuyện. Nhìn vào số 3 ở hàng dưới cùng. Cách duy nhất chúng tôi có thể đạt được điều này là:

  • Chúng tôi bắt đầu ở đó, hoặc
  • Chúng tôi di chuyển đến đó, theo trình tự di chuyển 8 giờ và 10 giờ. Nhưng nếu lần di chuyển cuối cùng là lúc 8 giờ (đúng như vậy, vì chúng ta có thể di chuyển theo bất kỳ thứ tự nào), thì chúng ta phải đi qua (3,1), khoảng cách thực sự là 2 (bạn có thể xem từ bản cheatsheet gốc). Vì vậy, những gì chúng ta nên làm là theo dõi một lần di chuyển 8 giờ, tiết kiệm tổng cộng hai lần di chuyển.

Có một sự tối ưu hóa tương tự được thực hiện với 4 ở trên cùng bên phải. Ngoài việc bắt đầu từ đó, cách duy nhất để đạt được điều đó là di chuyển 8 giờ từ (4,3). Điều đó không có trong bảng gian lận, nhưng nếu nó ở đó, khoảng cách của nó sẽ là 3, vì thay vào đó chúng ta có thể có 7 o'clocked đến (3,1), khoảng cách chỉ là 2. Vì vậy, chúng ta nên theo dõi lại một Di chuyển 8 giờ và sau đó đi tiếp một hướng 7 giờ.

Vì vậy, chúng ta cần thêm một số nữa vào bảng cheats:

. . 4
. 2 . 2
0 3 2

(Lưu ý: có rất nhiều cách tối ưu theo dõi ngược từ (0,1) và (0,2) nhưng vì trình giải sẽ không bao giờ đưa chúng ta đến đó nên chúng ta không cần phải lo lắng về chúng.)

Vì vậy, đây là một số mã Python để đánh giá điều này:

def knightDistance (x, y):
    # normalise the coordinates
    x, y = abs(x), abs(y)
    if (x<y): x, y = y, x
    # now 0 <= y <= x

    # n8 means (-2,-1) (8 o'clock), n7 means (-1,-2) (7 o'clock), n10 means (-2,+1) (10 o'clock)
    if (x>2*y):
        # we're below the midline.  Using 8- & 10-o'clock moves
        n7, n8, n10 = 0,  (x + 2*y)//4,  (x - 2*y + 1)//4
    else:
        # we're above the midline.  Using 7- and 8-o'clock moves
        n7, n8, n10 = (2*y - x)//3, (2*x - y)//3,  0
    x -= 2*n8 + n7 + 2*n10
    y -= n8 + 2*n7 - n10
    # now 0<=x<=2, and y <= x.  Also (x,y) != (2,1)

    # Try to optimise the paths.
    if (x, y)==(1, 0): # hit the  3.  Did we need to?
        if (n8>0): # could have passed through the 2 at 3,1.  Back-up
            x, y = 3, 1; n8-=1;
    if (x, y)==(2, 2): # hit the 4.  Did we need to?
        if (n8>0): # could have passed through a 3 at 4,3.  Back-up, and take 7 o'clock to 2 at 3,1
            x, y = 3, 1; n8-=1; n7+=1

    # Almost there.  Now look up the final leg
    cheatsheet = [[0, 3, 2], [2, None, 2], [4]]
    return n7 + n8 + n10 + cheatsheet [y][x-y]

Ngẫu nhiên, nếu bạn muốn biết một tuyến đường thực tế, thì thuật toán này cũng cung cấp điều đó: nó chỉ đơn giản là sự liên tiếp của n7 lần di chuyển 7 giờ, tiếp theo là (hoặc xen kẽ với) n8 lần di chuyển 8 giờ, n10 10- giờ di chuyển, và bất kỳ điệu nhảy nào được quy định bởi bảng gian lận (bản thân nó, có thể nằm trong bảng gian lận).

Bây giờ: Làm thế nào để chứng minh điều này là đúng. Chỉ so sánh những kết quả này với một bảng các câu trả lời đúng là không đủ, vì bản thân vấn đề là không có giới hạn. Nhưng chúng ta có thể nói rằng, nếu khoảng cách của Hiệp sĩ của một ô vuông s là d, thì nếu {m} là tập hợp các bước di chuyển hợp pháp từ s, thì khoảng cách của Hiệp sĩ là (s + m) phải là d-1 hoặc d + 1 cho tất cả m. (Bạn có cần bằng chứng về điều này không?) Hơn nữa, phải có ít nhất một hình vuông như vậy có khoảng cách là d-1, trừ khi s là gốc. Vì vậy, chúng ta có thể chứng minh tính đúng đắn bằng cách hiển thị thuộc tính này được giữ cho mọi ô vuông. Như vậy:

def validate (n):

    def isSquareReasonable (x, y):
        d, downhills = knightDistance (x, y), 0
        moves = [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1,  2)]
        for dx, dy in moves:
            dd = knightDistance (x+dx,  y+dy)
            if (dd == d+1): pass
            elif (dd== d-1): downhills += 1
            else: return False;
        return (downhills>0) or (d==0)

    for x in range (0,  n+1):
        for y in range (0,  n+1):
            if not isSquareReasonable (x,  y): raise RuntimeError ("Validation failed")

Ngoài ra, chúng ta có thể chứng minh tính đúng đắn của một ô vuông bất kỳ bằng cách đuổi theo tuyến đường từ xuống dốc đến điểm gốc. Đầu tiên, kiểm tra tính hợp lý của s như trên, sau đó chọn s + m bất kỳ sao cho khoảng cách (s + m) == d-1. Lặp lại cho đến khi chúng tôi đạt được điểm gốc.

Howzat?


2
/*
This program takes two sets of cordinates on a 8*8 chessboard, representing the
starting and ending points of a knight's path.
The problem is to print the cordinates that the knight traverses in between, following
the shortest path it can take.
Normally this program is to be implemented using the Djikstra's algorithm(using graphs)
but can also be implemented using the array method.
NOTE:Between 2 points there may be more than one shortest path. This program prints
only one of them.
*/

#include<stdio.h>

#include<stdlib.h>

#include<conio.h>

int m1=0,m2=0;

/*
This array contains three columns and 37 rows:
The rows signify the possible coordinate differences.
The columns 1 and 2 contains the possible permutations of the row and column difference 
between two positions on a chess board;
The column 3 contains the minimum number of steps involved in traversing the knight's 
path with the given permutation*/

int arr[37][3]={{0,0,0},{0,1,3},{0,2,2},{0,3,3},{0,4,2},{0,5,3},{0,6,4},{0,7,5},    {1,1,2},{1,2,1},{1,3,2},{1,4,3},{1,5,4},{1,6,3},{1,7,4},{2,2,4},{2,3,3},{2,4,2},
            {2,5,3},{2,6,3},{2,7,5},{3,3,2},{3,4,3},{3,5,4},{3,6,3},{3,7,4},{4,4,4},{4,5,3},{4,6,4},{4,7,5},{5,5,4},{5,6,5},{5,7,4},{6,6,5},{6,7,5},{7,7,6}};

void printMoves(int,int,int,int,int,int);
void futrLegalMove(int,int,int,int);
main()
{
  printf("KNIGHT'S SHORTEST PATH ON A 8*8 CHESSBOARD :\n");
  printf("------------------------------------------");
  printf("\nThe chessboard may be treated as a 8*8 array here i.e. the (1,1) ");
  printf("\non chessboard is to be referred as (0,0) here and same for (8,8) ");
  printf("\nwhich is to be referred as (7,7) and likewise.\n");
  int ix,iy,fx,fy;
  printf("\nEnter the initial position of the knight :\n");
  scanf("%d%d",&ix,&iy);
  printf("\nEnter the final position to be reached :\n");
  scanf("%d%d",&fx,&fy);
  int px=ix,py=iy;
  int temp;
  int tx,ty;
  printf("\nThe Knight's shortest path is given by :\n\n");
  printf("(%d, %d)",ix,iy);
  futrLegalMove(px,py,m1,m2);
  printMoves(px,py,fx,fy,m1,m2);
   getch();
} 

/*
  This method checkSteps() checks the minimum number of steps involved from current
  position(a & b) to final position(c & d) by looking up in the array arr[][].
*/

int checkSteps(int a,int b,int c,int d)
{  
    int xdiff, ydiff;
    int i, j;
    if(c>a)
        xdiff=c-a;
    else
        xdiff=a-c;
    if(d>b)
        ydiff=d-b;
    else
        ydiff=b-d;
    for(i=0;i<37;i++)
        {
            if(((xdiff==arr[i][0])&&(ydiff==arr[i][1])) || ((xdiff==arr[i][1])&& (ydiff==arr[i] [0])))
            {
                j=arr[i][2];break;
            }
        }

        return j;
}   

/*
This method printMoves() prints all the moves involved.
*/

void printMoves(int px,int py, int fx, int fy,int a,int b)
{    
 int temp;
 int tx,ty;
 int t1,t2;
  while(!((px==fx) && (py==fy)))
  {   
      printf(" --> ");
      temp=checkSteps(px+a,py+b,fx,fy);
      tx=px+a;
      ty=py+b;
      if(!(a==2 && b==1))
      {if((checkSteps(px+2,py+1,fx,fy)<temp) && checkMove(px+2,py+1))
      {temp=checkSteps(px+2,py+1,fx,fy);
       tx=px+2;ty=py+1;}}
      if(!(a==2 && b==-1))
      {if((checkSteps(px+2,py-1,fx,fy)<temp) && checkMove(px+2,py-1))
      {temp=checkSteps(px+2,py-1,fx,fy);
       tx=px+2;ty=py-1;}}
      if(!(a==-2 && b==1))
      {if((checkSteps(px-2,py+1,fx,fy)<temp) && checkMove(px-2,py+1))
      {temp=checkSteps(px-2,py+1,fx,fy);
       tx=px-2;ty=py+1;}}
      if(!(a==-2 && b==-1))
      {if((checkSteps(px-2,py-1,fx,fy)<temp) && checkMove(px-2,py-1))
      {temp=checkSteps(px-2,py-1,fx,fy);
       tx=px-2;ty=py-1;}}
      if(!(a==1 && b==2))
      {if((checkSteps(px+1,py+2,fx,fy)<temp) && checkMove(px+1,py+2))
      {temp=checkSteps(px+1,py+2,fx,fy);
       tx=px+1;ty=py+2;}}
      if(!(a==1 && b==-2))
      {if((checkSteps(px+1,py-2,fx,fy)<temp) && checkMove(px+1,py-2))
      {temp=checkSteps(px+1,py-2,fx,fy);
       tx=px+1;ty=py-2;}}
      if(!(a==-1 && b==2))
      {if((checkSteps(px-1,py+2,fx,fy)<temp) && checkMove(px-1,py+2))
      {temp=checkSteps(px-1,py+2,fx,fy);
       tx=px-1;ty=py+2;}}
      if(!(a==-1 && b==-2))
      {if((checkSteps(px-1,py-2,fx,fy)<temp) && checkMove(px-1,py-2))
      {temp=checkSteps(px-1,py-2,fx,fy);
       tx=px-1;ty=py-2;}}
       t1=tx-px;//the step taken in the current move in the x direction.
       t2=ty-py;//" " " " " " " " " " " " " " " " " " " " " y " " " " ".
       px=tx;
       py=ty;
       printf("(%d, %d)",px,py);
       futrLegalMove(px,py,t1,t2);
       a=m1;
       b=m2;
   }

} 

/*
The method checkMove() checks whether the move in consideration is beyond the scope of
board or not.
*/   

int checkMove(int a, int b)
{
    if(a>7 || b>7 || a<0 || b<0)
        return 0;
    else
        return 1;
}

/*Out of the 8 possible moves, this function futrLegalMove() sets the valid move by
  applying the following constraints
      1. The next move should not be beyond the scope of the board.
      2. The next move should not be the exact opposite of the previous move.
  The 1st constraint is checked by sending all possible moves to the checkMove() 
  method;
  The 2nd constraint is checked by passing as parameters(i.e. a and b) the steps of the 
  previous move and checking whether or not it is the exact opposite of the current move.
*/

void futrLegalMove(int px,int py,int a,int b)
{
     if(checkMove(px+2,py+1) && (a!=-2 && b!=-1))
         m1=2,m2=1;
     else
     {
         if(checkMove(px+2,py-1)&& (a!=-2 && b!=1))
             m1=2,m2=-1;
     else
     {
         if(checkMove(px-2,py+1)&& (a!=2 && b!=-1))
              m1=-2,m2=1;
     else
     {
         if(checkMove(px-2,py-1)&& (a!=2 && b!=1))
               m1=-2,m2=-1;
     else
     {
         if(checkMove(px+1,py+2)&& (b!=-2 && a!=-1))
               m2=2,m1=1;
     else
     {
         if(checkMove(px+1,py-2)&& (a!=-1 && b!=2))
               m2=-2,m1=1;
     else
     {
         if(checkMove(px-1,py+2)&& (a!=1 && b!=-2))
               m2=2,m1=-1;
     else
     {
         if(checkMove(px-1,py-2)&& (a!=1 && b!=2))
               m2=-2,m1=-1;
     }}}}}}}
}

//End of Program.

Tôi chưa nghiên cứu đồ thị..vì vấn đề triển khai nó thông qua các mảng đơn giản, tôi không thể tìm ra giải pháp nào khác ngoài điều này. Tôi coi các vị trí không phải là xếp hạng và tệp (Ký hiệu cờ vua thông thường), mà là chỉ số mảng. FYI, cái này chỉ dành cho bàn cờ 8 * 8. Mọi lời khuyên cải tiến luôn được hoan nghênh.

* Các ý kiến ​​phải đủ để bạn hiểu về logic. Tuy nhiên, bạn luôn có thể hỏi.

* Đã kiểm tra trên trình biên dịch DEV-C ++ 4.9.9.2 (Phần mềm đổ máu).


2

Tôi nghĩ rằng điều này cũng có thể giúp bạn ..

NumWays(x,y)=1+min(NumWays(x+-2,y-+1),NumWays(x+-1,y+-2)); 

và sử dụng Lập trình động để có giải pháp.

Tái bút: Nó gần như sử dụng BFS mà không cần phải khai báo các nút và cạnh của đồ thị.


1

Đây là một giải pháp cho vấn đề cụ thể này được thực hiện trong Perl. Nó sẽ hiển thị một trong những con đường ngắn nhất - có thể có nhiều hơn một trong một số trường hợp.

Tôi đã không sử dụng bất kỳ thuật toán nào được mô tả ở trên - nhưng sẽ rất tuyệt nếu so sánh nó với các giải pháp khác.

#!/usr/local/bin/perl -w

use strict;

my $from = [0,0];
my $to   = [7,7];

my $f_from = flat($from);
my $f_to   = flat($to);

my $max_x = 7;
my $max_y = 7;
my @moves = ([-1,2],[1,2],[2,1],[2,-1],[1,-2],[-1,-2],[-2,-1],[-2,1]);
my %squares = ();
my $i = 0;
my $min = -1;

my @s = ( $from );

while ( @s ) {

   my @n = ();
   $i++;

   foreach my $s ( @s ) {
       unless ( $squares{ flat($s) } ) {
            my @m = moves( $s );
            push @n, @m;
            $squares{ flat($s) } = { i=>$i, n=>{ map {flat($_)=>1} @m }, };

            $min = $i if $squares{ flat($s) }->{n}->{$f_to};
       }
   }

   last if $min > -1;
   @s = @n;
}

show_path( $f_to, $min );

sub show_path {
    my ($s,$i) = @_;

    return if $s eq $f_from;

    print "$i => $f_to\n" if $i == $min;

    foreach my $k ( keys %squares ) {
       if ( $squares{$k}->{i} == $i && $squares{$k}->{n}->{$s} ) {
            $i--;
            print "$i => $k\n";
            show_path( $k, $i );
            last;
       }
    }
}

sub flat { "$_[0]->[0],$_[0]->[1]" }

sub moves {
    my $c = shift;
    my @s = ();

    foreach my $m ( @moves ) {
       my $x = $c->[0] + $m->[0];
       my $y = $c->[1] + $m->[1];

       if ( $x >= 0 && $x <=$max_x && $y >=0 && $y <=$max_y) {
           push @s, [$x, $y];
       }
    }
    return @s;
}

__END__

1
public class Horse {

    private int[][] board;
    private int[] xer = { 2, 1, -1, -2, -2, -1, 1, 2 };
    private int[] yer = { 1, 2, 2, 1, -1, -2, -2, -1 };
    private final static int A_BIG_NUMBER = 10000;
    private final static int UPPER_BOUND = 64;


    public Horse() {
        board =  new int[8][8];
    }

    private int solution(int x, int y, int destx, int desty, int move) {

        if(move == UPPER_BOUND) {
            /* lets put an upper bound to avoid stack overflow */
            return A_BIG_NUMBER;
        }

        if(x == 6 && y ==5) {
            board[6][5] = 1;
            return 1;
        }
        int min = A_BIG_NUMBER;
        for (int i = 0 ; i < xer.length; i++) {
            if (isMoveGood(x + xer[i], y + yer[i])) {
                if(board[x + xer[i]][y + yer[i]] != 0) {
                    min = Integer.min(min, 1 + board[x +xer[i]] [y +yer[i]]);                   
                } else {
                    min = Integer.min(min, 1 + solution(x + xer[i], y + yer[i], destx, desty, move + 1));   
                }                   
            }
        }   
        board[x][y] = min;
        return min;
    }


    private boolean isMoveGood(int x, int y) {
        if (x >= 0 && x < board.length && y >= 0 && y < board.length)
            return true;
        return false;
    }


    public static void main(String[] args) {

        int destX = 6;
        int destY = 7;
        final Horse h = new Horse();
        System.out.println(h.solution(0, 0, destX, destY, 0));
    }
}

0

Chỉ cần mã ruby ​​từ jsfiddle của Graeme Pyle ở trên , đánh dấu tất cả các mã bổ sung và chuyển đổi còn lại thành ruby ​​chỉ để nhận giải pháp bằng thuật toán của anh ấy, có vẻ như đang hoạt động. Vẫn đang thử nghiệm mặc dù:

def getBoardOffset(board)
  return board.length / 2
end

def setMoveCount(x, y, count, board)
  offset = getBoardOffset(board)
  board[y + offset][x + offset] = count
end

def getMoveCount(x, y, board)
    offset = getBoardOffset(board)
    row = board[y + offset]
    return row[x + offset]
end

def isBottomOfVerticalCase(x, y)
    return (y - 2 * x) % 4 == 0
end

def isPrimaryDiagonalCase(x, y)
    return (x + y) % 2 == 0
end

def isSecondaryDiagonalCase(x, y)
    return (x + y) % 2 == 1
end

def simplifyBySymmetry(x, y)
    x = x.abs
    y = y.abs
    if (y < x)
      t = x
      x = y
      y = t
    end
    return {x: x, y: y}
end

def getPrimaryDiagonalCaseMoveCount(x, y)
    var diagonalOffset = y + x
    var diagonalIntersect = diagonalOffset / 2
    return ((diagonalIntersect + 2) / 3).floor * 2
end

def getSpecialCaseMoveCount(x, y)
    specials = [{
            x: 0,
            y: 0,
            d: 0
        },
        {
            x: 0,
            y: 1,
            d: 3
        },
        {
            x: 0,
            y: 2,
            d: 2
        },
        {
            x: 0,
            y: 3,
            d: 3
        },
        {
            x: 2,
            y: 2,
            d: 4
        },
        {
            x: 1,
            y: 1,
            d: 2
        },
        {
            x: 3,
            y: 3,
            d: 2
        }
    ];
    matchingSpecial=nil
    specials.each do |special|
      if (special[:x] == x && special[:y] == y)
        matchingSpecial = special
      end
    end
    if (matchingSpecial)
      return matchingSpecial[:d]
    end
end

def isVerticalCase(x, y)
  return y >= 2 * x
end

def getVerticalCaseMoveCount(x, y)
    normalizedHeight = getNormalizedHeightForVerticalGroupCase(x, y)
    groupIndex = (normalizedHeight/4).floor
    groupStartMoveCount = groupIndex * 2 + x
    return groupStartMoveCount + getIndexInVerticalGroup(x, y)
end

def getIndexInVerticalGroup(x, y)
    return getNormalizedHeightForVerticalGroupCase(x, y) % 4
end

def getYOffsetForVerticalGroupCase(x) 
    return x * 2
end

def getNormalizedHeightForVerticalGroupCase(x, y)
    return y - getYOffsetForVerticalGroupCase(x)
end

def getSecondaryDiagonalCaseMoveCount(x, y)
    diagonalOffset = y + x
    diagonalIntersect = diagonalOffset / 2 - 1
    return ((diagonalIntersect + 2) / 3).floor * 2 + 1
end

def getMoveCountO1(x, y)
    newXY = simplifyBySymmetry(x, y)
    x = newXY[:x]
    y = newXY[:y]
    specialMoveCount = getSpecialCaseMoveCount(x ,y)
    if (specialMoveCount != nil)
      return specialMoveCount
    elsif (isVerticalCase(x, y))
      return getVerticalCaseMoveCount(x ,y)
    elsif (isPrimaryDiagonalCase(x, y))
      return getPrimaryDiagonalCaseMoveCount(x ,y)
    elsif (isSecondaryDiagonalCase(x, y))
      return getSecondaryDiagonalCaseMoveCount(x ,y)
    end
end

def solution(x ,y)
  return getMoveCountO1(x, y)
end


puts solution(0,0)

Mục đích duy nhất là tiết kiệm thời gian cho ai đó chuyển đổi mã nếu ai đó cần mã đầy đủ.


0

đây là phiên bản PHP của hàm Jules May

function knightDistance($x, $y)
{
    $x = abs($x);
    $y = abs($y);

    if($x < $y)
    {
        $tmp = $x;
        $x = $y;
        $y = $tmp;
    }

    if($x > 2 * $y)
    {
        $n7 = 0;
        $n8 = floor(($x + 2*$y) / 4);
        $n10 = floor(($x - 2*$y +1) / 4);
    }
    else
    {
        $n7 = floor((2*$y - $x) / 3);
        $n8 = floor((2*$x - $y) / 3);
        $n10 = 0;
    }

    $x -= 2 * $n8 + $n7 + 2 * $n10;
    $y -= $n8 + 2 * $n7 - $n10;

    if($x == 1 && $y == 0)
    {
        if($n8 > 0)
        {
            $x = 3;
            $y = 1;
            $n8--;
        }
    }
    if($x == 2 && $y == 2)
    {
        if($n8 > 0)
        {
            $x = 3;
            $y = 1;
            $n8--;
            $n7++;
        }
    }

    $cheatsheet = [[0, 3, 2], [2, 0, 2], [4]];

    return $n7 + $n8 + $n10 + $cheatsheet [$y][$x-$y];
}

0

Đây là chương trình của tôi. Đây không phải là một giải pháp hoàn hảo. Có rất nhiều thay đổi cần thực hiện trong hàm đệ quy. Nhưng kết quả cuối cùng này là hoàn hảo. Tôi đã cố gắng tối ưu hóa một chút.

public class KnightKing2 {
    private static int tempCount = 0;

    public static void main(String[] args) throws IOException {
        Scanner in = new Scanner(System.in);
        int ip1 = Integer.parseInt(in.nextLine().trim());
        int ip2 = Integer.parseInt(in.nextLine().trim());
        int ip3 = Integer.parseInt(in.nextLine().trim());
        int ip4 = Integer.parseInt(in.nextLine().trim());
        in.close();
        int output = getStepCount(ip1, ip2, ip3, ip4);
        System.out.println("Shortest Path :" + tempCount);

    }

    // 2 1 6 5 -> 4
    // 6 6 5 5 -> 2

    public static int getStepCount(int input1, int input2, int input3, int input4) {
        return recurse(0, input1, input2, input3, input4);

    }

    private static int recurse(int count, int tx, int ty, int kx, int ky) {

        if (isSolved(tx, ty, kx, ky)) {
            int ccount = count+1;
            System.out.println("COUNT: "+count+"--"+tx+","+ty+","+ccount);
            if((tempCount==0) || (ccount<=tempCount)){
                tempCount = ccount;
            }
            return ccount;
        }

            if ((tempCount==0 || count < tempCount) && ((tx < kx+2) && (ty < ky+2))) {
                if (!(tx + 2 > 8) && !(ty + 1 > 8)) {
                    rightTop(count, tx, ty, kx, ky);

                }
                if (!(tx + 2 > 8) && !(ty - 1 < 0)) {
                    rightBottom(count, tx, ty, kx, ky);
                }
                if (!(tx + 1 > 8) && !(ty + 2 > 8)) {
                    topRight(count, tx, ty, kx, ky);
                }
                if (!(tx - 1 < 0) && !(ty + 2 > 8)) {
                    topLeft(count, tx, ty, kx, ky);
                }
                if (!(tx + 1 > 8) && !(ty - 2 < 0)) {
                     bottomRight(count, tx, ty, kx, ky);
                }
                if (!(tx - 1 < 0) && !(ty - 2 < 0)) {
                     bottomLeft(count, tx, ty, kx, ky);
                }
                if (!(tx - 2 < 0) && !(ty + 1 > 8)) {
                    leftTop(count, tx, ty, kx, ky);
                }
                if (!(tx - 2 < 0) && !(ty - 1 < 0)) {
                    leftBottom(count, tx, ty, kx, ky);
                }
            }

        return count;

    }

    private static int rightTop(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx + 2, ty + 1, kx, ky);

    }

    private static int topRight(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx + 1, ty + 2, kx, ky);
    }

    private static int rightBottom(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx + 2, ty - 1, kx, ky);
    }

    private static int bottomRight(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx + 1, ty - 2, kx, ky);
    }

    private static int topLeft(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx - 1, ty + 2, kx, ky);
    }

    private static int bottomLeft(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx - 1, ty - 2, kx, ky);
    }

    private static int leftTop(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx - 2, ty + 1, kx, ky);
    }

    private static int leftBottom(int count, int tx, int ty, int kx, int ky) {
        return count + recurse(count + 1, tx - 2, ty - 1, kx, ky);
    }

    private static boolean isSolved(int tx, int ty, int kx, int ky) {
        boolean solved = false;
        if ((tx == kx) && (ty == ky)) {
            solved = true;
        } else if ((tx + 2 == kx) && (ty + 1 == ky)) { // right top
            solved = true;
        } else if ((tx + 2 == kx) && (ty - 1 == ky)) { // right bottom
            solved = true;
        } else if ((ty + 2 == ky) && (tx + 1 == kx)) {// top right
            solved = true;
        } else if ((ty + 2 == ky) && (tx - 1 == kx)) {// top left
            solved = true;
        } else if ((tx - 2 == kx) && (ty + 1 == ky)) { // left top
            solved = true;
        } else if ((tx - 2 == kx) && (ty - 1 == ky)) {// left bottom
            solved = true;
        } else if ((ty - 2 == ky) && (tx + 1 == kx)) { // bottom right
            solved = true;
        } else if ((ty - 2 == ky) && (tx - 1 == kx)) { // bottom left
            solved = true;
        }

        return solved;
    }

}

1
Nó có thể được tối ưu hóa hơn nữa để tránh trùng lặp.
Arun

-1

Đây là phiên bản C dựa trên mã Mustafa Serdar Şanlı hoạt động cho bảng finit:

#include <stdio.h>
#include <math.h>

#define test(x1, y1, x2, y2) (sx == x1 && sy == y1 &&tx == x2 &&ty == y2) || (sx == x2 && sy == y2 && tx == x1 && ty==y1)

int distance(int sx, int sy, int tx, int ty) {
    int x, y, t;
    double delta;

    // special corner cases 
    if (test(1, 1, 2, 2) || 
        test(7, 7, 8, 8) || 
        test(7, 2, 8, 1) || 
        test(1, 8, 2, 7))
        return 4;

    // axes symmetry 
    x = abs(sx - tx);
    y = abs(sy - ty);

    // diagonal symmetry 
    if (x < y) {
        t = x;
        x = y;
        y = t;
    }

    // 2 corner cases
    if (x == 1 && y == 0)
        return 3;
    if (x == 2 && y == 2)
        return 4;

    // main
    delta = x - y;
    if (y > delta) {
        return (int)(delta - 2 * floor((delta - y) / 3));
    }
    else {
        return (int)(delta - 2 * floor((delta - y) / 4));
    }
}

Kiểm tra nó ở đây với bằng chứng chống lại một giải pháp đệ quy


1
Kiểm tra một số lượng hữu hạn các trường hợp không phải là một bằng chứng.
xay sinh tốBender
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.