Найкоротший шлях лицаря на шаховій дошці


95

Я практикувався на майбутніх змаганнях з програмування і натрапив на запитання, яке мене просто розгубило. Однак я відчуваю, ніби це концепція, якій я повинен навчитися зараз, а не схрещувати пальці, що вона ніколи не виникає.

В основному це стосується фігури лицаря на шаховій дошці. Вам дано два входи: початкове місце і кінцеве місце. Мета полягає в тому, щоб розрахувати і надрукувати найкоротший шлях, яким лицар може пройти, щоб дістатися до цільового місця.

Я ніколи не мав справи з найкоротшим шляхом, і навіть не знаю, з чого почати. Яку логіку я використовую для вирішення цього питання?

PS Якщо це має будь-яке значення, вони хочуть, щоб ви доповнили звичайні ходи лицаря, також дозволяючи йому переміщатися до чотирьох кутів квадрата, утворених (потенційно) восьми ходами, які може зробити лицар, враховуючи, що центр квадрата є місцезнаходження лицаря.


Не могли б ви уточнити ПС? Ви маєте на увазі, якщо лицар знаходиться на Е4, він може перейти до С2, С6, G2 та G6?
Стів Тьоа

Так, крім це звичайні рухи.
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:

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*_ може бути швидшим.
  2. найшвидший спосіб - це попередньо розрахувати всі відстані та зберегти їх у повну матрицю 8x8. ну, я б назвав це обманом і працює лише тому, що проблема невелика. Але іноді змагання перевіряють, наскільки швидко працює ваша програма.
  3. Головне, якщо ви готуєтесь до змагань з програмування, ви повинні знати загальні алгоритми, включаючи Дейкстру. Гарною відправною точкою є читання Introduction to AlgorithmsISBN 0-262-03384-4. Або ви можете спробувати wikipedia, http://en.wikipedia.org/wiki/List_of_algorithms

Це здається складним порівняно з рішенням Мустафи, представленим нижче.
lpapp

Що ви маєте на увазі під недоступним кроком? Лицар може дійти до будь-якої площі праворуч!
everlasto

51

EDIT: Дивіться відповідь симона , де він виправив формулу, представлену тут.

Насправді існує формула 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 );
}

Примітка: Це питання було задано у день 1 SACO 2007.
Рішення тут


8
Будь-який шанс ви могли б описати, як ви розробляли цю формулу?
kybernetikos

3
Цей код працює? Якщо лицар позиціонує на (0,0), і я хочу перенести його до точки (1,0). Це задовольняє 0 <= y <= x. дельта = 1-0 = 1. y не більша за дельта (0 <1). Це означає, що я йду за іншою справою. дельта - 2 * ((дельта - у) / 4) = 1-2 ((1-0) / 4) = 1-1 / 2 = 1. Я не пересуваюсь, я можу перемістити лицаря з (0,0) до (1,0) одним рухом. Питання, чи працює цей алгоритм? Або що я роблю неправильно?
SimpleApp

3
Схоже, це працює тільки для прямих можливих позицій. Але якщо користувач надає (2,2), він повертає 0, а якщо користувач надає (4,4), він повертає 2, які помиляються.
юни

6
Має бути 2 * floor((y - delta) / 3) + deltaі delta - 2 * floor((delta - y) / 4). Це офіційне рішення з цієї сторінки конкурсу, але це неправильно. Це перше рівняння (від if) повертає неправильні відповіді. На шаховій дошці [-1000..1000] х [-1000..1000], яка 2001x2001 велика (але логічно необмежена), дана відповідь нараховує 2,669,329 з 4,004,001 поля правильними (66,66%). Хтось знає робоче рішення без жодних циклів?
Robo Robok

2
Я згоден, це рішення не працює. Дивіться інші відповіді, такі як stackoverflow.com/a/26888893/4288232 для робочого рішення O (1).
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).
Джон Хаскалл

Правильно, я змінив свою відповідь, щоб включити припущення про нескінченну шахову дошку
Graeme Pyle

@JonatasWalker Будь ласка, поясніть, я не бачу проблем, що переходять від (8,0) до (0,0). Це займає 4 ходи?
Graeme Pyle

Вибачте @GraemePyle, я винен, видаливши коментар.
Джонатас Уокер

2
привіт @GraemePyle - Я згоден з вами, це найкращий загальний підхід до програмування. Чудова діаграма до речі!
Fattie

22

Дуже цікава проблема, з якою я стикався недавно. Переглянувши деякі рішення, я намагався відновити аналітичну формулу ( O(1) time and space complexity), подану на рішеннях SACO 2007 Day 1 .

Перш за все я хочу оцінити Грейма Пайла за дуже гарну візуалізацію, яка допомогла мені виправити формулу.

Чомусь (можливо, для спрощення або краси чи просто помилки) вони перейшли 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);
}

Формула працює для всіх пар (x, y) (після застосування осей та діагональної симетрії) за винятком (1,0) та (2,2) кутових випадків, які не задовольняють шаблону та жорстко кодуються у наступному фрагменті:

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 Функція "відстань" є аналітичною функцією і може обчислити кількість кроків для заданої (x, y) позиції за O (1) час. В основному в цій формулі ми не залежимо від дошки (я думаю, під "нескінченною дошкою" ви маєте на увазі це майно), отже, це буде працювати.
симон

2
@simon Чи може хтось пояснити головну формулу, будь ласка? Мені важко пояснити це простими словами
MarBVI

1
@MarBVI Якщо ми наближаємось до лінії y = x, ми можемо зменшувати x + y на 3 у кожному русі, залишаючись поруч з лінією y = x. Якщо ми знаходимось біля рядка y = 0, ми можемо зменшити х на 2 у кожному русі, залишаючись біля y = 0 рядка. Ось чому у нас є 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 та спростимо основну формулу, отримаємо формулу: якщо (y> x / 2), то {return (x + y) / 3} else {return x / 2}
simon

1
@simon чудово, це робить більш зрозумілим ... ти на свій час :)
MarBVI

1
про всяк випадок у конкурсі BAPC2017 також було питання під назвою Knight's Marathon на нескінченній дошці, яке ця формула чудово вирішує. 2017.bapc.eu/files/preliminaries_problems.pdf
Амір-Мусаві,

19

Так, Dijkstra та BFS отримають відповідь, але я думаю, що шаховий контекст цієї проблеми дає знання, які можуть отримати рішення, яке набагато швидше, ніж загальний алгоритм найкоротшого шляху, особливо на нескінченній шаховій дошці.

Для простоти опишемо шахову дошку як площину (x, y). Мета - знайти найкоротший шлях від (x0, y0) до (x1, y1), використовуючи лише кроки-кандидати (+ -1, + -2), (+ -2, + -1) та (+ -2 , + -2), як описано в ПС питання

Ось нове спостереження: намалюйте квадрат з кутами (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4) . Цей набір (називайте його S4) містить 32 пункти. Найкоротший шлях від будь-якої з цих 32 точок до (x, y) вимагає рівно двох ходів .

Найкоротший шлях від будь-якої з 24 точок у наборі S3 (визначений аналогічно) до (x, y) вимагає щонайменше двох ходів .

Тому, якщо | x1-x0 |> 4 або | y1-y0 |> 4, найкоротший шлях від (x0, y0) до (x1, y1) рівно на два ходи більше, ніж найкоротший шлях від (x0, y0) до 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 пункти ". Ні, ні, він містить 81, оскільки це 9х9 квадрат? Також "Нехай N = max (| x1-x0 |, | y1-y0 |). Якщо N> = 4, то найкоротший шлях від (x0, y0) до (x1, y1) має стелю (N / 2) кроки ". Це неправда, візьмемо для прикладу x0 = 0, y0 = 0, x1 = 4, y1 = 4, найкоротший шлях - 4, а не 2, як це говорить формула.
сатоші

1
(1) Набір стосується лише точок на межі самого квадрата. Це 32 пункти / місця. (2) Якщо взяти до уваги PS плаката щодо додаткових ходів (див. Також коментарі у вихідному повідомленні), мінімальна кількість ходів стає двома.
Стів Тьоа

Дякую, це зараз має сенс :)
satoshi

а якщо дошка нескінченна? в цьому випадку добре працюватиме лише BFS
Олег Абражаєв,

@SteveTjoa вибачте, я не можу зрозуміти, чому ви згадали (+ -2, + -2) переїзд, оскільки це неможливо для лицаря
Павел Белий

12

Відповідь O (1) вище [ https://stackoverflow.com/a/8778592/4288232 Мустафи Сердар Шані] насправді не працює. (Перевірте (1,1) або (3,2) або (4,4), окрім очевидних крайових випадків (1,0) або (2,2)).

Нижче наведено набагато потворніше рішення (python), яке справді працює (з доданими "тестами"):

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насправді не працює на SO.
Петро Пеллер

1
Ось моя версія в python 2/3. Я намагався спростити функцію вирішення, але це непросто! gist.github.com/TimSC/8b9a80033f3a22a708a4b9741931c591
TimSC

9

Що вам потрібно зробити, це мислити можливі рухи лицаря як графік, де кожна позиція на дошці - це вузол, а можливі переміщення в іншу позицію як край. Не потрібно алгоритму Дейкстри, тому що кожен край має однакову вагу або відстань (всі вони такі ж легкі чи короткі). Ви можете просто здійснити пошук BFS від початкової точки до досягнення кінцевої позиції.


1
+ !, для цієї конкретної проблеми BFS достатньо.
TiansHUо

3
BFS може бути достатньо, але звичайний BST підірветься для багатьох запитів - вам потрібно буде кешувати, які квадрати ви відвідали. І тоді BFS починає виглядати трохи як алгоритм Дейкстри ...
Чарльз Стюарт

Що було б найкращим способом відстежити всю позицію, яку ми вже подорожували, щоб дерево BFS зростало лише вперед, і коли ми виявляємо вузли, доступні з нової точки, ми в кінцевому підсумку не додаємо старіший вузол знову ... і застрягаємо в нескінченна петля!
Nitish Upreti

Я тут припускаю, що ми можемо просто зробити, зберігаючи останню позицію лицаря?
Nitish Upreti

7

Рішення з перших принципів в Python

Я вперше зіткнувся з цією проблемою в тесті на кодивність. Вони дали мені 30 хвилин, щоб це вирішити - мені довелося значно довше, ніж це, щоб дійти до цього результату! Проблема полягала в тому, скільки рухів потрібно, щоб лицар перейшов від 0,0 до x, y використовуючи лише легальні ходи лицаря. x і y були більш-менш необмеженими (тому ми не говоримо тут про просту шахівницю 8х8).

Вони хотіли рішення O (1). Я хотів рішення, коли програма чітко вирішила проблему (тобто я хотів чогось більш очевидного правильного, ніж зразок Graeme - шаблони мають звичку ламатися там, де ти не дивишся), і мені дуже хотілося не покладатися на неаргументована формула, як у розчині Мустафи

Отже, ось моє рішення, чого воно варте. Почніть, як і інші, відзначаючи, що рішення є симетричним щодо осей та діагоналей, тому нам потрібно вирішити лише для 0> = y> = x. Для простоти пояснення (і коду) я збираюся відмінити проблему: лицар починається з x, y і націлений на 0,0.

Припустимо, ми зменшимо проблему до сусідства з початком. Ми з часом дізнаємось про те, що насправді означає "vicinty", але поки що давайте просто запишемо деякі рішення в шпаргалку (походження внизу зліва):

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

Отже, враховуючи х, у на сітці, ми можемо просто зчитувати кількість переміщень до початку координат.

Якщо ми почали поза сіткою, нам доведеться повернутися до неї. Ми вводимо "середню лінію", яка є лінією, представленою y = x / 2. Будь-який лицар у х, у на цій лінії може пройти назад до шпаргалки, використовуючи серію 8-годинних рухів (тобто: (-2, -1) ходи). Якщо х, у лежить над середньою лінією, тоді нам буде потрібно послідовне переміщення 8 годин і 7 годин, а якщо воно лежить нижче середньої лінії, нам буде потрібна послідовність 8 годин і 10 o 'годинник рухається. Тут слід зазначити дві речі:

  • Ці послідовності є очевидно найкоротшими шляхами. (Хочете, щоб я це довів, чи це очевидно?)
  • Ми дбаємо лише про кількість таких ходів. Ми можемо змішувати та узгоджувати рухи в будь-якому порядку.

Отже, давайте розглянемо вищевикладені ходи. Ми стверджуємо, що:

  • (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).

(Чи хотіли б ви докази цього?) Отже, відстань лицаря буде сумою n7, n8, n10 та шпаргалки [x-dx, y-dy], і наша шпаргалка зводиться до цього:

. . 4
. 2 .
0 3 2

Зараз це не зовсім кінець історії. Подивіться на 3 в нижньому ряду. Єдині способи досягти цього:

  • Ми почали там, або
  • Ми переїхали туди, послідовно рухаючись 8 годин та 10 годин. Але якщо останній хід був о 8 годині (на що це має право, оскільки ми можемо робити свої кроки в будь-якому порядку), то ми повинні були пройти через (3,1), відстань якого насправді 2 (як ви можете див. з оригінального шпаргалки). Отже, ми повинні зробити зворотний трек за 8 годин, заощаджуючи два ходи.

Існує подібна оптимізація, яку слід провести з 4 вгорі праворуч. Крім того, щоб почати туди, єдиний спосіб дістатися до нього - 8 годинний хід від (4,3). Це не на шпаргалці, але якби він був там, його відстань склала б 3, тому що ми могли б замість цього 7 заблокувати до (3,1), що має відстань лише 2. Отже, ми повинні відстежувати його 8-годинний рух, а потім ідіть вперед на один 7-годинний.

Отже, нам потрібно додати ще одне число до шпаргалки:

. . 4
. 2 . 2
0 3 2

(Примітка. Існує цілий набір оптимізацій зворотного відстеження від (0,1) та (0,2), але оскільки вирішувач ніколи нас не візьме, нам не потрібно про них турбуватися.)

Тож ось, ось якийсь код Python для оцінки цього:

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 для всіх м. (Вам потрібні докази цього?) Крім того, повинен бути принаймні один такий квадрат, відстань якого d-1, якщо s не є початком. Отже, ми можемо довести правильність, показавши цю властивість для кожного квадрата. Таким чином:

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 вниз до початку. Спочатку перевірте s на розумність, як зазначено вище, потім виберіть будь-який s + m, такий, що відстань (s + m) == d-1. Повторюйте, поки ми не досягнемо походження.

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.

Я ще не вивчав графіки. Зважаючи на проблему їх реалізації через просто масиви, я не міг отримати іншого рішення, крім цього. Я ставився до позицій не як до рангів і файлів (звичайна шахова нотація), а як до індексів масиву. FYI, це лише для шахової дошки 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)); 

і за допомогою динамічного програмування отримати рішення.

PS: Це начебто використовує 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

Просто рубіновий код із відповіді грайм Пейл вище , смугастий увесь додатковий код та перетворений залишився в рубін лише для отримання рішення за його алгоритмом, здається, працює. Проте все ще тестуємо:

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

ось 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
Його можна додатково оптимізувати, щоб уникнути дублікатів.
Арун

-1

Ось версія C, заснована на коді Мустафи Сердара Шані, який працює на дошці 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));
    }
}

Слід перевірити це на підтвердження проти рекурсивного рішення


1
Перевірка кінцевої кількості справ не є доказом.
BlenderBender
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.