http://acm.pku.edu.cn/JudgeOnline/problem?id=1178

   Задача: Камелот

   Давным давно король Артур и рыцари Круглого Стола обычно собирались на новый год, чтобы отпраздновать свою дружбу. В память об этих событиях придумали настольную игру "Камелот" для одного игрока, в которой Король и несколько фигур Рыцарей произвольно располагаются в различных клетках доски.

   Доска имеет размер 8х8 клеток, как показано ниже на рисунке.

http://acm.pku.edu.cn/JudgeOnline/images/1178_1.jpg

   Король может перемещаться в любую смежную клетку доски из закрашенной окружности в не закрашенную, как показано на рисунке Figure 2, при этом не выходя за пределы доски.

   Рыцарь может перемещаться из клетки с закрашенной окружностью в одну из клеток с незакрашенной окружностью, как показано на рисунке Figure 3, при этом не выходя за пределы доски.

   Во время игры может поместить более одной фигуры в одну клетку. Клетки считаются достаточно большими, и не возникает препятствий для свободного перемещения фигур.

   Игроку необходимо так перемещать фигуры Короля и Рыцарей, чтобы собрать их всех в какой-то одной клетке за наименьшее число ходов. Ходы фигурами необходимо делать по правилам, описанным выше. Дополнительно к этому, если Король и оди или более Рыцарей находятся в одной клетке, игрок может Короля и одного из рыцарей переместить вместе по правилам перемещения Рыцаря и считать это одним ходом.

   Напишите программу для вычисления минимального количества ходов, необходимых для перемещения всех фигур в одну клетку доски.

   Входные данные

   Все входные данне размещены в одной строке, описывающей начальное расоложение фигур на доске. Строка сожержит последовательность клеток доски, первая из которых - клетка Короля, остальные - клетки Рыцарей. Каждая клетка описывается парой "буква-цифра". Буква обозначает горизонтальную, а цифра - вертикальную координату клетки доски. Все фигуры в начале игры расположены в разных клетках.

   0 <= количество Рыцарей <= 63

   Выходные данные 

   Вывести единственную  строку с положительным целым числом, обозначающим минимальное число ходов игрока, необходимых для перемещения всех фигур в одну клетку доски.

   Пример входных данных

D4A3A8H1H8

   Пример выходных данных

10

   Идея решения

   Методом "волны" (обходом в ширину) подсчитаем минимальное количество ходов, за которое Рыцарь может попасть из клетки с координатами (x, y) в клетку (i, j). Сделаем это для всех пар клеток и запомним результаты в четырехмерной матрице p[1..8, 1..8, 1..8, 1..8]. Количество ходов для Короля из клетки (x, y) в клетку (i, j) равно Max(Abs(i-x), Abs(j-y)). Если запретить совместное перемещение Короля и Рыцаря, то задача с водится к поиску клетки (i, j), сумма расстояний до которого от клеток, занимаемых Королем и Рыцарями, имеет минимальное значение. Предположим, что в клетке (xp, yp) Король встречается с Рыцарем, имеющим номер k, и дальнейшее перемещение до клетки (i, j) они выполняют совместно. В этом случае требуется подсчитать количество ходов Короля от клетки (xk, yk) до (xp, yp) и прибавить к нему величины p[x[k], y[k], xp, yp] и p[xp, yp, i, j]. Проделав эту работу для всех пар (i, j) и (xp, yp), не забывая при этом фиксировать минимальную оценку, мы получим искомый результат.

   Зная алгоритмическую классику - задачи о расстановке ферзей, обхода конем шахматной доски, поиска выхода из лабиринта и т.п. - решение данной задачи найти не сложно. Ее можно рассматривать как обобщение на идейном уровне и использовать для самостоятельной работы при изучении темы "Перебор и методы его сокращения". Интерпретация же доски в виде графа позволяет установить связь с такими известными алгоритмами поиска кратчайших путей, как алгоритмы Дейкстра и Флойда, именно последний использован нами в примере решения на С++.

   Решение на Pascal:

Код:
const TW = 8;
      TW2 = 2*TW;
      NKMAX = 63;
      UNKNOWN = -1;

type  Board    = array[0..TW-1,0..TW-1] of integer;
      BigBoard = array[0..TW2-1,0..TW2-1] of integer;
      BigString= string[255];

var NK : integer;                        (* # knights *)
    IP : array[0..2*NKMAX-1] of integer; (* input *)
    MCM: array[0..NKMAX-1] of Board;     (* cost matrices for matings *)
    KingMat : Board;
    KMats   : array[0..TW-1,0..TW-1] of Board; (* all-pos knights costs *)
    KBoard: BigBoard;

function CL(c:char):integer; (* chess letter *)
begin
  CL := ord(c) - ord('A');
end;

function CD(c:char):integer; (* chess digit *)
begin
  CD := ord(c) - ord('1');
end;

procedure Erase(var t:Board);
var h,v:integer;
begin
  for h:=0 to TW-1 do
    for v:=0 to TW-1 do
      t[h,v] := UNKNOWN;
end;

procedure AddBoard(a,b: Board; var c:Board);
var v,h:integer;
begin
  for v:=0 to TW-1 do
    for h:=0 to TW-1  do
      c[h,v] := a[h,v]+b[h,v];
end;

procedure KnightCosts(h,v,s:integer; var t:Board);
begin
    if(h>=0) and (h<TW) and (v>=0) and (v<TW) then
    if (t[h,v]<0) or (s<t[h,v]) then
        begin
         t[h,v] := s;
         inc(s);
         KnightCosts(h+2,v+1,s,t);
         KnightCosts(h+2,v-1,s,t);
         KnightCosts(h-2,v+1,s,t);
         KnightCosts(h-2,v-1,s,t);
         KnightCosts(h+1,v+2,s,t);
         KnightCosts(h+1,v-2,s,t);
         KnightCosts(h-1,v+2,s,t);
         KnightCosts(h-1,v-2,s,t);
        end;
end;

procedure KingCosts(h,v,s:integer; var t:Board);
var j:integer;
begin
  if(h>=0) and (h<TW) and (v>=0) and (v<TW) then
   if (t[h,v]<0) or (s<t[h,v]) then
    begin
       t[h,v] := s;
       inc(s);
       KingCosts(h,v+1,s,t);
       KingCosts(h,v-1,s,t);
       for j:=-1 to 1 do
         begin
           KingCosts(h+1,v+j,s,t);
           KingCosts(h-1,v+j,s,t);
         end
    end
end;

(* PreCompute Knight cost matrices for all 64 board positions *)

procedure ComputeKnightMats;
var h,v:integer;
begin
  for v := 0 to TW-1 do
    for h := 0 to TW-1 do
      begin
        Erase(KMats[h,v]);
        KnightCosts(h,v,0,KMats[h,v]);
      end;
end;

(* The cost matrix for Knight at h,v *)

procedure GetKnightCostMat(h,v:integer; var k:Board);
begin
  k := KMats[h,v];
end;

(* Minimum value of Board and Position *)

function MinVal(k:Board):integer;
var v,h,S:integer;
begin
  S:=k[0,0];
  for v:=0 to TW-1 do
    for h:=0 to TW-1 do
      if(k[h,v]<S) then
        S:=k[h,v];
  MinVal := S;
end;

(* Compute the matings cost matrices for all NK knights *)
(*                                                      *)
(* MCM[i] = combined cost of king mating Knight i       *)

procedure MakePairs;
var i,ii,h,v: integer;
    tb,km,tu,kmt:Board;
begin
  for i:=0 to NK-1 do
    begin
      ii := i*2;
      GetKnightCostMat(IP[ii],IP[ii+1],km);
      AddBoard(km,KingMat,tb);
      for h:=0 to TW-1 do
        for v:=0 to TW-1 do
          begin
            GetKnightCostMat(h,v,kmt);
            AddBoard(tb,kmt,tu);
            MCM[i][h,v]:=MinVal(tu);
          end;
    end
end;

procedure Results(val:integer);
var i:integer;
begin
  writeln(val);
end;

(* Pick the Best *)

function Best:integer;
var allknights,rn,someknight: Board;
    i,newval,val,ii,h,v: integer;

begin

  (* first case, no gathering *)

  GetKnightCostMat(IP[0],IP[1],allknights);

  ii := 2;
  for i:=1 to NK-1 do
    begin
      GetKnightCostMat(IP[ii],IP[ii+1],someknight);
      AddBoard(allknights,someknight,allknights);
      inc(ii,2);
    end;

  (* rs = sum of all knight matrices *)

  AddBoard(allknights,KingMat,rn);

  val := MinVal(rn);    (* first guess, no matings *)

  (* now, the NK possible matings *)

  ii := 0;
  for i:=0 to NK-1 do  (* mate with knight i *)
    begin
      GetKnightCostMat(IP[ii],IP[ii+1],someknight);
      (* someknight = costs of Knight i *)

      for h:=0 to TW-1 do   (* rn = rs - kt + MCM[i] *)
        for v:=0 to TW-1 do
          rn[h,v] := allknights[h,v] - someknight[h,v] + MCM[i][h,v];

      newval := MinVal(rn);
      if(newval<val)then
        val := newval;
      inc(ii,2);
    end;
  {writeln(val);}
  Best :=val;
end;


procedure ReadPieces;
var  buf: bigstring;
     i  : integer;
begin
  readln(buf);
  KingCosts(CL(buf[1]),CD(buf[2]),0,KingMat);
  i := 3; NK := 0;
  while(i<length(buf))do
    begin
      IP[i-3]:=CL(buf[i]);
      inc(i);
      IP[i-3]:=CD(buf[i]);
      inc(i);
      inc(NK);
    end;
end;

procedure InitData;
begin
  Erase(KingMat);
  ComputeKnightMats;
end;

begin
  InitData;
  ReadPieces;
  MakePairs;
  Results(Best)
end.

Решение на С++:

Код:
#include <iostream>
using namespace std;

const int inf = 100000;
char str[150];
int k[64],king[64][64],knight[64][64];
int move1[8][2]={-1,-1,-1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1};
int move2[8][2]={-1,-2,-2,-1,-2,1,-1,2,1,2,2,1,2,-1,1,-2};

void init(){
    int i,j,x,y,tx,ty;
    for(i=0;i<64;i++)
        for(j=0;j<64;j++)
            if(i==j) king[i][j]=knight[i][j]=0;
            else king[i][j]=knight[i][j]=inf;
    for(i=0;i<64;i++){
        x=i/8,y=i%8;
        for(j=0;j<8;j++){
            tx=x+move1[j][0],ty=y+move1[j][1];
            if(tx>=0 && ty>=0 && tx<8 && ty<8)
                king[i][8*tx+ty]=1;
        }
    }
    for(i=0;i<64;i++){
        x=i/8,y=i%8;
        for(j=0;j<8;j++){
            tx=x+move2[j][0],ty=y+move2[j][1];
            if(tx>=0 && ty>=0 && tx<8 && ty<8)
                knight[i][8*tx+ty]=1;
        }
    }
}
void floyd1(){
    int i,j,k;
    for(k=0;k<64;k++)
        for(i=0;i<64;i++)
            for(j=0;j<64;j++)
                if(king[i][k]+king[k][j]<king[i][j])
                    king[i][j]=king[i][k]+king[k][j];
}
void floyd2(){
    int i,j,k;
    for(k=0;k<64;k++)
        for(i=0;i<64;i++)
            for(j=0;j<64;j++)
                if(knight[i][k]+knight[k][j]<knight[i][j])
                    knight[i][j]=knight[i][k]+knight[k][j];
}
int main(){
    int i,j,l,cnt,pos,sum,ans,len,t1,t2;
    init();
    floyd1();
    floyd2();
    while(scanf("%s",str)!=EOF){
        len=strlen(str);
        pos=(str[0]-'A')+(str[1]-'1')*8;
        cnt=(len-2)/2;
        if(cnt==0){
            printf("0\n");
            continue;
        }
        for(i=0,j=2;i<cnt;i++,j+=2)
            k[i]=(str[j]-'A')+(str[j+1]-'1')*8;
        for(ans=inf,i=0;i<64;i++){
            for(sum=l=0;l<cnt;l++)
                sum+=knight[k[l]][i];
            for(j=0;j<64;j++){
                t1=king[pos][j];
                for(t2=inf,l=0;l<cnt;l++)
                    t2=min(t2,knight[k[l]][j]+knight[j][i]-knight[k[l]][i]);
                ans=min(ans,sum+t1+t2);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}