체스 판에서 기사의 최단 경로


95

나는 다가오는 프로그래밍 대회를 위해 연습을 해왔고 완전히 어리둥절한 질문을 우연히 발견했습니다. 하지만 결코 떠오르지 않는다는 손가락을 꼬집기보다는 지금 배워야 할 개념 인 것 같습니다.

기본적으로 체스 판에있는 기사를 다룹니다. 시작 위치와 끝 위치의 두 가지 입력이 제공됩니다. 목표는 기사가 목표 위치에 도달하기 위해 취할 수있는 최단 경로를 계산하고 인쇄하는 것입니다.

나는 최단 경로 같은 것을 다루지 않았고 어디서부터 시작해야할지조차 모른다. 이 문제를 해결하기 위해 어떤 논리를 사용합니까?

추신 : 만약 관련성이 있다면, 그들은 광장의 중심이 다음과 같을 때 기사가 만들 수있는 (잠재적으로) 8 번의 동작으로 형성된 사각형의 네 모서리로 이동할 수 있도록 허용하여 기사의 정상적인 동작을 보완하기를 원합니다. 기사의 위치.


추신을 명확히 할 수 있습니까? 기사가 E4에 있으면 C2, C6, G2, G6로 이동할 수 있다는 뜻입니까?
Steve Tjoa

예, 정상적인 동작에 더해.
Kyle Hughes

1
다음은 문제의 수학적 분석입니다. math.stackexchange.com/questions/104700/…
Graeme Pyle

답변:


28

사용 가능한 모든 이동이 연결되고 (값 = 1) 사용할 수없는 이동이 연결 해제 된 (값 = 0) 그래프가 있습니다. 희소 행렬은 다음과 같습니다.

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

그래프에서 두 점의 최단 경로는 http://en.wikipedia.org/wiki/Dijkstra's_algorithm을 사용하여 찾을 수 있습니다 .

wikipedia-page의 의사 코드 :

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[]

편집하다:

  1. 바보처럼 http://en.wikipedia.org/wiki/A*_algorithm을 사용하는 것이 더 빠를 수 있다고 말했습니다 .
  2. 가장 빠른 방법은 모든 거리를 미리 계산하여 8x8 풀 매트릭스에 저장하는 것입니다. 음, 나는 그것을 속임수라고 부르고 문제가 작기 때문에 작동합니다. 그러나 때로는 대회가 프로그램 실행 속도를 확인합니다.
  3. 요점은 프로그래밍 대회를 준비하고 있다면 Dijkstra를 포함한 일반적인 알고리즘을 알아야한다는 것입니다. 좋은 시작점은 Introduction to AlgorithmsISBN 0-262-03384-4를 읽는 것 입니다. 또는 wikipedia, http://en.wikipedia.org/wiki/List_of_algorithms를 사용해 볼 수 있습니다 .

이것은 아래 Mustafa의 솔루션에 비해 복잡해 보입니다.
lpapp 2014

이동 불가능이란 무엇을 의미합니까? 기사는 어떤 광장에도 닿을 수 있습니다!?
everlasto

51

편집 : 여기에 제시된 공식을 수정 한 simon의 대답을 참조 하십시오.

실제로 O (1) 공식이 있습니다.

이것은 내가 (기사가 N에 도달 할 수있는 사각형을 시각화에 적용했지만 이미지입니다 번째 움직임을 같은 색으로 칠해진됩니다). 기사의 움직임

여기서 패턴을 알 수 있습니까?

패턴을 볼 수 있지만 f( x , y )정사각형에서 정사각형 ( 0 , 0 )으로 이동하는 데 필요한 이동 횟수를 반환하는 함수를 찾기가 정말 어렵습니다.( x , y )

하지만 다음과 같은 경우에 작동하는 공식이 있습니다. 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 );
}

참고 :이 질문은 SACO 2007 Day 1 에 요청되었으며
솔루션은 여기에 있습니다.


8
그 공식을 어떻게 계산했는지 설명해 주시겠습니까?
kybernetikos

3
이 코드가 작동합니까? 기사가 (0,0) 위치에 있고 나는 그것을 (1,0) 지점으로 옮기고 싶습니다. 이것은 0 <= y <= x를 충족합니다. 델타 = 1-0 = 1. y는 델타보다 크지 않습니다 (0 <1). 이것은 내가 다른 경우에 갈 것임을 의미합니다. 델타-2 * ((델타-y) / 4) = 1-2 ((1-0) / 4) = 1-1 / 2 = 1. 한 번의 이동으로 기사를 (0,0)에서 (1,0)으로 이동할 수있는 이유는 없습니다. 이 알고리즘이 작동 하는가? 아니면 내가 뭘 잘못하고 있니?
SimpleApp 2015

3
직접 가능한 위치에서만 작동하는 것 같습니다. 그러나 사용자가 (2,2)를 제공하면 0을 반환하고 사용자가 (4,4)를 제공하면 잘못된 2를 반환합니다.
yunas

6
2 * floor((y - delta) / 3) + delta및 이어야합니다 delta - 2 * floor((delta - y) / 4). 이것은이 콘테스트 페이지의 공식 솔루션이지만 잘못되었습니다. 이 첫 번째 방정식 (에서 if)은 잘못된 답을 반환합니다. 2001x2001 크기 (논리적으로 무제한) 인 체스 판 [-1000..1000] x [-1000..1000]에서 주어진 답은 4,004,001 개 필드 중 2,669,329 개가 맞습니다 (66.66 %). 누구나 루프가없는 작업 솔루션을 알고 있습니까?
Robo Robok

2
이 솔루션이 작동하지 않는다는 데 동의합니다. 작동하는 O (1) 솔루션에 대해서는 stackoverflow.com/a/26888893/4288232 와 같은 다른 답변을 참조하십시오 .
TimSC

45

여기에 올바른 O (1) 솔루션이 있지만, 기사가 체스 기사처럼 움직이며 무한 체스 판에서 움직이는 경우 :

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

이것을 찾는 열쇠는 보드를 그릴 때 나타나는 패턴을 알아 차리는 것입니다. 아래 다이어그램에서 사각형의 숫자는 해당 사각형에 도달하는 데 필요한 최소 이동 횟수입니다 (폭 우선 검색을 사용하여 찾을 수 있음).

패턴

솔루션은 축과 대각선에 대칭이기 때문에 x> = 0 및 y> = x 케이스 만 그렸습니다.

왼쪽 하단 블록은 시작 위치이며 블록의 숫자는 해당 블록에 도달하기위한 최소 이동 횟수를 나타냅니다.

주목해야 할 3 가지 패턴이 있습니다.

  • 증가하는 파란색 세로 그룹 4 개
  • "기본"빨간색 대각선 (백 슬래시처럼 왼쪽 상단에서 오른쪽 하단으로 실행)
  • "보조"녹색 대각선 (빨간색과 같은 방향)

(두 대각선 세트가 왼쪽 상단에서 오른쪽 하단으로 표시되는지 확인하십시오. 이동 횟수가 일정합니다. 왼쪽 하단 상단 오른쪽 대각선은 훨씬 더 복잡합니다.)

각각에 대한 공식을 유도 할 수 있습니다. 노란색 블록은 특별한 경우입니다. 따라서 솔루션은 다음과 같습니다.

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);

}

가장 어려운 것은 수직 그룹입니다.

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);

}

다른 경우는 바이올린을 참조하십시오.

내가 놓친 더 단순하거나 더 우아한 패턴이 있을까요? 그렇다면 나는 그들을보고 싶다. 특히 파란색 세로 케이스에서 대각선 패턴을 발견했지만 아직 살펴 보지 않았습니다. 그럼에도 불구하고이 솔루션은 여전히 ​​O (1) 제약 조건을 충족합니다.


이것은 (문자 그대로) 코너 케이스를 처리하지 않는 것 같습니다. "0"이 보드의 왼쪽 아래 사각형 (a1)이면 가장 가까운 "2"공간 (b2)에 두 번 이동할 수 없습니다. 그렇게하려면 첫 번째 이동은 (a3)의 왼쪽에있는 존재하지 않는 공간으로 이동해야합니다.
John Hascall 2016 년

그래, 나는 무한한 체스 보드 가정을 포함하는 내 대답을 변경
그레엄 PYLE

@JonatasWalker 설명해주세요. (8,0)에서 (0,0)으로가는 문제가 보이지 않습니다. 4 개의 동작이 필요합니까?
Graeme Pyle

죄송합니다 @GraemePyle, 내 잘못, 내 댓글을 삭제했습니다.
Jonatas Walker

2
안녕하세요 @GraemePyle-이것이 최고의 전반적인 프로그래밍 접근 방식이라는 데 동의합니다. 그건 그렇고 멋진 다이어그램!
Fattie 2011

22

최근에 만난 매우 흥미로운 문제입니다. 몇 가지 솔루션을 찾은 후 SACO 2007 Day 1 솔루션O(1) time and space complexity 에 제공된 분석 공식 ( ) 을 복구하려고했습니다 .

먼저 수식을 수정하는 데 도움이 된 매우 멋진 시각화 에 대해 Graeme Pyle 에 감사드립니다 .

어떤 이유로 (단순화 또는 아름다움 또는 실수 로 인한 것일 수 있음) 운영자 로 minus로그인하여 floor잘못된 공식을 얻었습니다 floor(-a) != -floor(a) for any a.

다음은 올바른 분석 공식입니다.

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

이 공식은 (1,0) 및 (2,2) 코너 케이스를 제외하고 모든 (x, y) 쌍 (축 및 대각선 대칭을 적용한 후)에 대해 작동하며, 패턴을 충족하지 않고 다음 코드 조각에서 하드 코딩됩니다.

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>

참고 : 설명 용으로 만 사용 된 jQuery는 코드를 참조하십시오 distance.


2
@OlegAbrazhaev "거리"함수는 분석 함수이며 O (1) 시간에 주어진 (x, y) 위치에 대한 단계 수를 계산할 수 있습니다. 기본적으로이 공식에서 우리는 보드에 의존하지 않습니다 ( "무한 보드"는 당신이 그 속성을 의미한다고 생각합니다), 따라서 작동 할 것입니다.
simon

2
@simon 누군가 주요 공식을 설명해 주시겠습니까? 간단한 단어로 설명 할 수 어려운 찾는 미안
MarBVI

1
@MarBVI 만약 우리가 y = x 라인 근처에 있다면 우리는 y = x 라인 근처에 머무르면서 움직일 때마다 x + y를 3만큼 줄일 수 있습니다. y = 0 라인에 가까워지면 y = 0 라인 근처에 머물면서 모든 움직임에서 x를 2만큼 줄일 수 있습니다. 그렇기 때문에 2 개의 경우가 있습니다. 더 정확하게는 특정 라인에 가깝게 말하는 것입니다. 1. y = x 라인에 가깝다는 것은 y = x 및 y = x / 2 라인으로 제한된 섹션을 의미합니다 (y> x / 2 ). 2. y = 0 라인 근처 란 y = 0 및 y = x / 2 라인 (y <x / 2)에 의해 제한된 구간을 의미합니다. 위에서 언급 한 모든 것을 취하고 Math.floor를 제거하고 주 공식을 단순화하면 다음 공식을 얻게됩니다. if (y> x / 2) then {return (x + y) / 3} else {return x / 2}
simon

1
@simon 대단해, ​​그것은 당신의 시간 동안 더 명확하게 ... ty :)
MarBVI

1
혹시라도 BAPC2017 콘테스트에는이 공식이 완벽하게 해결한다는 무한 보드에서 Knight 's Marathon이라는 질문이있었습니다. 2017.bapc.eu/files/preliminaries_problems.pdf
Amir-Mousavi

19

예, Dijkstra와 BFS가 답을 얻을 수 있지만,이 문제의 체스 컨텍스트는 특히 무한 체스 판에서 일반적인 최단 경로 알고리즘보다 훨씬 빠른 솔루션을 산출 할 수있는 지식을 제공한다고 생각합니다.

간단하게 체스 판을 (x, y) 평면으로 설명하겠습니다. 목표는 후보 단계 (+ -1, + -2), (+ -2, + -1) 및 (+ -2) 만 사용하여 (x0, y0)에서 (x1, y1)까지의 최단 경로를 찾는 것입니다. , + -2), 질문의 PS에 설명 된대로

다음은 새로운 관찰입니다 : 모서리가있는 사각형 그리기 (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4) . 이 세트 (S4라고 부름)는 32 점을 포함합니다. 이 32 개 지점에서 (x, y)까지의 최단 경로는 정확히 두 번 이동 해야 합니다 .

세트 S3 (유사하게 정의 됨)의 24 개 지점 중 하나에서 (x, y)까지의 최단 경로 는 최소 두 번의 이동이 필요 합니다 .

따라서 | x1-x0 |> 4 또는 | y1-y0 |> 4 인 경우 (x0, y0)에서 (x1, y1)까지의 최단 경로는 (x0, y0)에서 (x0, y0)까지의 최단 경로보다 정확히 2 번 더 S4. 그리고 후자의 문제는 간단한 반복으로 신속하게 해결할 수 있습니다.

N = max (| x1-x0 |, | y1-y0 |)라고합시다. N> = 4이면 (x0, y0)에서 (x1, y1)까지의 최단 경로에 ceil (N / 2) 단계가 있습니다.


1
이 대답에 대해 혼란스러운 것은 나뿐입니까? "모서리가있는 사각형을 그립니다 (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4). S4)는 32 점을 포함합니다. " 아니요, 9x9 정사각형이므로 81 개가 포함되어 있습니까? 또한 "Let N = max (| x1-x0 |, | y1-y0 |). N> = 4이면 (x0, y0)에서 (x1, y1)까지의 최단 경로는 ceil (N / 2)를 갖습니다. 단계. " 그것은 사실이 아닙니다. 예를 들어 x0 = 0, y0 = 0, x1 = 4, y1 = 4, 최단 경로는 그 공식이 제안하는 2가 아니라 4입니다.
satoshi 2015

1
(1) 세트는 사각형 자체의 경계에있는 점만을 나타냅니다. 32 개의 포인트 / 위치가 있습니다. (2) 추가 이동에 대한 포스터의 PS를 고려하면 (원래 게시물의 댓글도 참조) 최소 이동 수는 2 개가됩니다.
Steve Tjoa 2015

덕분에, 그 차종은 : 지금 감지
사토시

보드가 무한대라면 어떨까요? 이 경우에만 BFS는 잘 작동합니다
올렉 Abrazhaev

@SteveTjoa 죄송합니다. 기사에게는 불가능하므로 (+ -2, + -2) 이동을 언급 한 이유를 이해할 수 없습니다
Pavel Bely

12

위의 O (1) 답변 [ https://stackoverflow.com/a/8778592/4288232 by Mustafa Serdar Şanlı]이 실제로 작동하지 않습니다. ((1,1) 또는 (3,2) 또는 (4,4)를 확인하고 (1,0) 또는 (2,2)의 명백한 가장자리 사례를 제외)).

다음은 훨씬 더 추악한 솔루션 (파이썬)으로 작동합니다 ( "테스트"가 추가됨).

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
답변을 언급 above하거나 below실제로 작동하지 않습니다.
Petr Peller

1
다음은 파이썬 2/3 버전입니다. 해결 기능을 단순화하려고했지만 쉽지 않습니다! gist.github.com/TimSC/8b9a80033f3a22a708a4b9741931c591
TimSC

9

당신이해야 할 일은 기사의 가능한 움직임을 그래프로 생각하는 것인데, 보드의 모든 위치는 노드이고 가능한 이동은 다른 위치로 에지로 간주됩니다. 모든 모서리가 동일한 가중치 또는 거리를 갖기 때문에 dijkstra의 알고리즘이 필요하지 않습니다 (모두 수행하기 쉽고 짧습니다). 시작 지점에서 끝 위치에 도달 할 때까지 BFS 검색을 수행 할 수 있습니다.


1
+ !,이 특정 문제에 대해서는 BFS로 충분합니다.
TiansHUo

3
BFS로 충분할 수 있지만 일반 BST는 많은 쿼리에 대해 폭발 할 것입니다. 방문한 사각형을 캐시해야합니다. 그리고 BFS는 ... 다 익스트라의 알고리즘처럼 조금보고 시작
찰스 스튜어트

BFS 트리가 앞으로 만 성장하도록 이미 이동 한 모든 위치를 추적하는 가장 좋은 방법은 무엇이며 새 지점에서 사용할 수있는 노드를 발견 할 때 이전 노드를 다시 추가하지 않고 갇히게됩니다. 무한 루프!
Nitish Upreti

마지막 기사 위치를 저장하는 것만으로 할 수 있다고 생각합니다.
Nitish Upreti

7

Python의 첫 번째 원칙의 솔루션

나는 Codility 테스트에서이 문제를 처음 접했습니다. 그들은 그것을 해결하는 데 30 분을주었습니다.이 결과를 얻는 데는 그보다 훨씬 더 오래 걸렸습니다! 문제는 기사가 합법적 인 기사의 이동 만 사용하여 0,0에서 x, y로 이동하는 데 얼마나 많은 이동이 필요한지였습니다. x와 y는 다소 제한이 없었습니다 (따라서 여기서 간단한 8x8 체스 판에 대해 이야기하지 않습니다).

그들은 O (1) 솔루션을 원했습니다. 저는 프로그램이 문제를 명확하게 해결하는 솔루션을 원했습니다. Mustafa의 솔루션에서와 같이 논증되지 않는 공식

그래서 여기 내 솔루션이 있습니다. 다른 사람들과 마찬가지로 솔루션이 축과 대각선에 대해 대칭임을 주목하여 시작하십시오. 따라서 0> = y> = x에 대해서만 풀면됩니다. 설명 (및 코드)의 단순함을 위해 문제를 뒤집을 것입니다. 기사는 x, y에서 시작하여 0,0을 목표로합니다.

문제를 원점 근처로 축소한다고 가정 해 봅시다. 우리는 'vicinty'가 실제로 의미하는 바를 곧 알게 될 것이지만, 지금은 치트 시트에 몇 가지 해결책을 적어 보겠습니다 (왼쪽 하단에서 시작).

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

따라서 그리드에 x, y가 주어지면 원점으로 이동 한 횟수를 읽을 수 있습니다.

그리드 밖에서 시작했다면 다시 돌아 가야합니다. y = x / 2로 표시되는 선인 '중간 선'을 소개합니다. 해당 라인의 x, y에있는 모든 기사는 일련의 8시 동작 (즉 : (-2, -1) 동작)을 사용하여 치트 시트로 돌아갈 수 있습니다. x, y가 정중선 위에 있으면 8시와 7시 이동을 연속해야하고 정중선 아래에 있으면 8시와 10시의 연속이 필요합니다. '시계가 움직인다. 여기서 주목해야 할 두 가지 사항 :

  • 이러한 시퀀스는 가장 짧은 경로입니다. (증명하고 싶습니까, 아니면 명백합니까?)
  • 우리는 그러한 움직임의 수에만 관심이 있습니다. 어떤 순서로든 무브를 믹스 앤 매치 할 수 있습니다.

따라서 중간 선 위의 움직임을 살펴 보겠습니다. 우리가 주장하는 것은 다음과 같습니다.

  • (dx; dy) = (2,1; 1,2) (n8; n7) (수학 조판이없는 행렬 표기법-열 벡터 (dx; dy)는 열 벡터 (n8; n7)를 곱한 정사각형 행렬과 같습니다. 8시 이동 수 및 7시 이동 수) 및 유사하게;

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

나는 dx, dy가 대략 (x, y)이므로 (x-dx, y-dy)는 원점 근처에있을 것입니다 ( '근처'가 무엇이든간에).

이 용어를 계산하는 코드의 두 줄이 이에 대한 해결책이지만 몇 가지 유용한 속성을 갖도록 선택되었습니다.

  • 중간 선 위 공식은 (x, y)를 (0,0), (1,1) 또는 (2,2) 중 하나로 이동합니다.
  • 중간 선 아래 수식은 (x, y)를 (0,0), (1,0), (2,0) 또는 (1,1) 중 하나로 이동합니다.

(이것들의 증거를 원하십니까?) 따라서 Knight의 거리는 n7, n8, n10 및 치트 시트 [x-dx, y-dy]의 합이 될 것이며, 치트 시트는 다음과 같이 축소됩니다.

. . 4
. 2 .
0 3 2

자, 이것은 이야기의 끝이 아닙니다. 맨 아래 줄에있는 3을보십시오. 이에 도달 할 수있는 유일한 방법은 다음 중 하나입니다.

  • 우리는 거기서 시작했거나
  • 우리는 8시와 10시 이동의 순서로 그곳으로 이동했습니다. 그러나 마지막 움직임이 8 시라면 (어떤 순서로든 움직일 수 있기 때문에 자격이 있음), 거리가 실제로 2 인 (3,1)을 통과했을 것입니다. 원본 치트 시트에서 참조). 그래서 우리가해야 할 일은 8시 방향으로 한 번 뒤로 돌아가서 총 두 번의 동작을 줄이는 것입니다.

오른쪽 상단의 4와 유사한 최적화가 있습니다. 거기에서 시작하는 것 외에도 (4,3)에서 8시 방향으로 이동하는 것이 유일한 방법입니다. 그것은 치트 시트에 있지 않지만, 만약 그것이 있다면 그 거리는 3이 될 것입니다. 왜냐하면 우리는 7시를 (3,1)에 대신 할 수 있었기 때문입니다. 거리는 2입니다. 그래서 우리는 하나를 역 추적해야합니다. 8시 방향으로 이동 한 다음 7시 방향으로 이동합니다.

따라서 치트 시트에 숫자를 하나 더 추가해야합니다.

. . 4
. 2 . 2
0 3 2

(참고 : (0,1) 및 (0,2)에서 역 추적 최적화의 전체 부하가 있지만 솔버가 우리를 절대로 데려 가지 않을 것이기 때문에 걱정할 필요가 없습니다.)

그래서 여기에 이것을 평가하는 파이썬 코드가 있습니다 :

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]

덧붙여서, 실제 경로를 알고 싶다면이 알고리즘도 제공합니다. 이것은 단순히 n7 7시 이동의 연속이고 그 뒤에 n8 8시 이동, n10 10이 뒤 따르는 (또는 산재 된) 것입니다. 시 이동 및 치트 시트 (그 자체가 치트 시트에있을 수 있음)에 의해 지시되는 춤.

지금 : 이것이 옳다는 것을 증명하는 방법. 이 결과를 정답 표와 비교하는 것만으로는 충분하지 않습니다. 문제 자체는 제한이 없기 때문입니다. 그러나 우리는 기사의 제곱 s의 거리가 d라면 {m}이 s로부터의 합법적 인 이동의 집합이라면 기사의 거리 (s + m)는 d-1 또는 d + 1이어야한다고 말할 수 있습니다. 모든 m. (당신은 이것에 대한 증명이 필요합니까?) 또한, s가 원점이 아니라면 거리가 d-1 인 그러한 사각형이 적어도 하나 있어야합니다. 따라서 우리는이 속성이 모든 사각형에 대해 유지됨을 보여줌으로써 정확성을 증명할 수 있습니다. 그러므로:

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")

또는 내리막에서 출발지까지의 경로를 추적하여 어느 한 사각형의 정확성을 증명할 수 있습니다. 먼저 s의 합리성을 확인한 다음 거리 (s + m) == d-1이되는 s + m을 선택합니다. 원점에 도달 할 때까지 반복합니다.

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.

저는 아직 그래프를 연구하지 않았습니다. 단순히 배열을 통해 구현하는 문제에 따라 이것 외에는 어떤 해결책도 도출 할 수 없었습니다. 나는 순위와 파일 (일반적인 체스 표기법)이 아니라 배열 인덱스로 위치를 취급했습니다. 참고로, 이것은 8 * 8 체스 판 전용입니다. 모든 개선 조언은 항상 환영합니다.

* 논리에 대한 이해를 위해서는 주석으로 충분해야합니다. 그러나 항상 요청할 수 있습니다.

* DEV-C ++ 4.9.9.2 컴파일러 (Bloodshed 소프트웨어)에서 확인되었습니다.


2

이것도 도움이 될 것 같아요 ..

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

솔루션을 얻기 위해 동적 프로그래밍을 사용합니다.

추신 : 그래프의 노드와 에지를 선언하는 문제없이 BFS를 사용합니다.


1

다음은 Perl에서 구현 된이 특정 문제에 대한 해결책입니다. 가장 짧은 경로 중 하나가 표시됩니다. 경우에 따라 둘 이상이있을 수 있습니다.

위에서 설명한 알고리즘을 사용하지 않았지만 다른 솔루션과 비교하면 좋을 것입니다.

#!/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

Graeme Pyle의 답변 jsfiddle 위의 루비 코드 , 모든 추가 코드를 스트라이프하고 그의 알고리즘으로 솔루션을 얻기 위해 나머지를 루비로 변환하면 작동하는 것처럼 보입니다. 그래도 테스트 :

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)

유일한 의도는 누군가가 전체 코드를 필요로하는 경우 코드 변환 시간을 절약하는 것입니다.


0

Jules May 함수의 PHP 버전입니다.

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

여기 내 프로그램이 있습니다. 이것은 완벽한 해결책이 아닙니다. 재귀 함수에는 많은 변경 사항이 있습니다. 그러나이 최종 결과는 완벽합니다. 나는 약간의 최적화를 시도했습니다.

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
중복을 방지하기 위해 추가로 최적화 할 수 있습니다.
Arun

-1

다음은 finit 보드에서 작동하는 Mustafa Serdar Şanlı 코드를 기반으로 한 C 버전입니다.

#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));
    }
}

재귀 솔루션에 대한 증명으로 여기 에서 테스트하십시오.


1
한정된 수의 케이스를 테스트하는 것은 증거가 아닙니다.
BlenderBender
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.