เส้นทางที่สั้นที่สุดของอัศวินบนกระดานหมากรุก


96

ฉันฝึกซ้อมสำหรับการแข่งขันรายการที่กำลังจะมาถึงและฉันเจอคำถามที่ทำให้ฉันงงงวยไปหมด อย่างไรก็ตามฉันรู้สึกราวกับว่ามันเป็นแนวคิดที่ฉันควรเรียนรู้ในตอนนี้แทนที่จะข้ามนิ้วไปที่มันไม่มีวันเกิดขึ้น

โดยทั่วไปจะเกี่ยวข้องกับชิ้นส่วนอัศวินบนกระดานหมากรุก คุณได้รับปัจจัยการผลิตสองรายการ: ตำแหน่งเริ่มต้นและตำแหน่งสิ้นสุด เป้าหมายคือการคำนวณและพิมพ์เส้นทางที่สั้นที่สุดที่อัศวินสามารถใช้เพื่อไปยังสถานที่เป้าหมาย

ฉันไม่เคยจัดการกับสิ่งที่สั้นที่สุดเท่าที่ควรและฉันไม่รู้ด้วยซ้ำว่าจะเริ่มจากตรงไหน ฉันใช้ตรรกะอะไรในการจัดการกับปัญหานี้

ป.ล. หากมีความเกี่ยวข้องใด ๆ พวกเขาต้องการให้คุณเสริมการเคลื่อนไหวปกติของอัศวินโดยปล่อยให้มันเคลื่อนที่ไปที่มุมทั้งสี่ของจัตุรัสที่เกิดจากการเคลื่อนไหว (ที่เป็นไปได้) แปดท่าที่อัศวินสามารถทำได้เนื่องจากตรงกลางของจัตุรัสคือ ที่ตั้งของอัศวิน


คุณช่วยอธิบาย PS ได้หรือไม่? คุณหมายถึงถ้าอัศวินอยู่ใน 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

รหัสหลอกจากหน้าวิกิพีเดีย:

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 หรือคุณอาจลองใช้วิกิพีเดียhttp://en.wikipedia.org/wiki/List_of_algorithms

สิ่งนี้ดูเหมือนจะซับซ้อนเมื่อเทียบกับวิธีแก้ปัญหาของมุสตาฟาด้านล่าง
lpapp

คุณหมายถึงอะไรกับการย้ายที่ไม่พร้อมใช้งาน? อัศวินสามารถเข้าถึงจัตุรัสใดก็ได้!?
everlasto

51

แก้ไข: ดูคำตอบของ simonซึ่งเขาแก้ไขสูตรที่นำเสนอที่นี่

จริงๆแล้วมีสูตร O (1)

นี่คือภาพที่ผมได้ทำเพื่อให้มองเห็นมัน (สแควร์อัศวินสามารถเข้าถึงใน N THย้ายจะทาสีที่มีสีเดียวกัน) การเคลื่อนไหวของอัศวิน

คุณสามารถสังเกตรูปแบบที่นี่?

แม้ว่าเราจะเห็นรูปแบบ แต่ก็ยากที่จะหาฟังก์ชัน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 วันที่ 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) ได้ในครั้งเดียว คำถามนี้ algoritm ใช้งานได้หรือไม่? หรือฉันทำอะไรผิด?
SimpleApp

3
ดูเหมือนว่าจะใช้ได้กับตำแหน่งที่เป็นไปได้โดยตรงเท่านั้น แต่ถ้าผู้ใช้ระบุ (2,2) มันจะส่งกลับ 0 และถ้าผู้ใช้ระบุ (4,4) มันจะส่งคืน 2 ซึ่งผิด
ยูนา

6
มันควรจะเป็นและ2 * floor((y - delta) / 3) + delta delta - 2 * floor((delta - y) / 4)นี่เป็นวิธีแก้ปัญหาอย่างเป็นทางการจากหน้าการแข่งขันนี้ แต่ผิดพลาด สมการแรกนี้ (จากif) ส่งกลับคำตอบที่ผิด บนกระดานหมากรุก [-1000..1000] x [-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)
John Hascall

ใช่ฉันเปลี่ยนคำตอบเพื่อรวมสมมติฐานกระดานหมากรุกที่ไม่มีที่สิ้นสุด
Graeme Pyle

@JonatasWalker โปรดอธิบายฉันไม่เห็นปัญหาจาก (8,0) ถึง (0,0) ใช้เวลา 4 ท่า?
Graeme Pyle

ขอโทษ @GraemePyle ความผิดของฉันลบความคิดเห็นของฉัน
Jonatas Walker

2
สวัสดี @GraemePyle - ฉันเห็นด้วยกับคุณว่านี่เป็นแนวทางการเขียนโปรแกรมโดยรวมที่ดีที่สุด แผนภาพที่ยอดเยี่ยม!
Fattie

22

ปัญหาที่น่าสนใจมากที่ฉันพบเมื่อเร็ว ๆ นี้ หลังจากดูวิธีแก้ปัญหาแล้วฉันได้พยายามกู้คืนสูตรการวิเคราะห์ ( O(1) time and space complexity) ที่ให้ไว้ในโซลูชันSACO 2007 วันที่ 1 การแก้ปัญหา

ก่อนอื่นฉันอยากจะชื่นชม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);
}

สูตรนี้ใช้ได้กับคู่ (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 เราสามารถลด x ลง 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 และทำให้สูตรหลักง่ายขึ้นเราจะได้สูตรต่อไปนี้: if (y> x / 2) แล้ว {return (x + y) / 3} else {return x / 2}
simon

1
@simon เยี่ยมที่ทำให้ชัดเจนมากขึ้น ... ty สำหรับเวลาของคุณ :)
MarBVI

1
ในกรณีนี้การแข่งขัน BAPC2017 ยังมีคำถามชื่อ Knight's Marathon บนกระดานที่ไม่มีที่สิ้นสุดซึ่งสูตรนี้สามารถแก้ไขได้อย่างสมบูรณ์แบบ 2017bapc.eu/files/preliminaries_pro issues.pdf
Amir-Mousavi

19

ใช่ Dijkstra และ BFS จะให้คำตอบแก่คุณ แต่ฉันคิดว่าบริบทหมากรุกของปัญหานี้ให้ความรู้ที่สามารถให้โซลูชันที่เร็วกว่าอัลกอริธึมเส้นทางที่สั้นที่สุดทั่วไปโดยเฉพาะบนกระดานหมากรุกที่ไม่มีที่สิ้นสุด

เพื่อความง่ายขออธิบายกระดานหมากรุกเป็นระนาบ (x, y) เป้าหมายคือการค้นหาเส้นทางที่สั้นที่สุดจาก (x0, y0) ถึง (x1, y1) โดยใช้เฉพาะขั้นตอนของผู้สมัคร (+ -1, + -2), (+ -2, + -1) และ (+ -2 , + -2) ตามที่อธิบายไว้ใน PS ของคำถาม

ข้อสังเกตใหม่มีดังนี้วาดสี่เหลี่ยมที่มีมุม (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 = สูงสุด (| 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 เพราะมันเป็นสี่เหลี่ยม 9x9? นอกจากนี้ "ให้ N = max (| x1-x0 |, | y1-y0 |) ถ้า N> = 4 ดังนั้นเส้นทางที่สั้นที่สุดจาก (x0, y0) ถึง (x1, y1) มี ceil (N / 2) ขั้นตอน " นั่นไม่เป็นความจริงยกตัวอย่างเช่น x0 = 0, y0 = 0, x1 = 4, y1 = 4 เส้นทางที่สั้นที่สุดคือ 4 ไม่ใช่ 2 อย่างที่สูตรแนะนำ
satoshi

1
(1) เซตหมายถึงจุดบนขอบเขตของสี่เหลี่ยมเท่านั้น ที่มี 32 คะแนน / แห่ง. (2) เมื่อคุณคำนึงถึง PS ของผู้โพสต์เกี่ยวกับการเคลื่อนไหวเสริม (ดูความคิดเห็นในโพสต์ต้นฉบับด้วย) จำนวนขั้นต่ำของการเคลื่อนไหวจะกลายเป็นสอง
Steve Tjoa

ขอบคุณที่เข้าท่า :)
satoshi

จะเกิดอะไรขึ้นถ้ากระดานไม่มีที่สิ้นสุด? ในกรณีนี้ BFS เท่านั้นที่จะทำงานได้ดี
Oleg Abrazhaev

@SteveTjoa ขออภัยฉันไม่เข้าใจว่าทำไมคุณถึงพูดถึงการเคลื่อนไหว (+ -2, + -2) เนื่องจากเป็นไปไม่ได้สำหรับอัศวิน
Pavel Bely

12

คำตอบ O (1) ด้านบน [ https://stackoverflow.com/a/8778592/4288232โดย Mustafa Serdar Şanlı] ใช้ไม่ได้จริงๆ (ทำเครื่องหมาย (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
Petr Peller

1
นี่คือเวอร์ชันของฉันใน python 2/3 ฉันพยายามทำให้ฟังก์ชันแก้ง่ายขึ้น แต่มันไม่ง่ายเลย! gist.github.com/TimSC/8b9a80033f3a22a708a4b9741931c591
TimSC

9

สิ่งที่คุณต้องทำคือนึกถึงการเคลื่อนไหวที่เป็นไปได้ของอัศวินเป็นกราฟโดยที่ทุกตำแหน่งบนกระดานเป็นโหนดและการเคลื่อนที่ไปยังตำแหน่งอื่น ๆ ที่เป็นขอบ ไม่จำเป็นต้องใช้อัลกอริทึมของ dijkstra เนื่องจากทุกขอบมีน้ำหนักหรือระยะทางเท่ากัน (ทั้งหมดทำได้ง่ายหรือสั้น) คุณสามารถค้นหา BFS จากจุดเริ่มต้นของคุณจนกว่าคุณจะไปถึงตำแหน่งสิ้นสุด


1
+! สำหรับปัญหาเฉพาะนี้ BFS ก็เพียงพอแล้ว
TiansHUo

3
BFS อาจเพียงพอ แต่ BST ธรรมดาจะระเบิดสำหรับคำถามจำนวนมากคุณจะต้องแคชสี่เหลี่ยมที่คุณเคยเยี่ยมชม จากนั้น BFS ก็เริ่มดูเหมือนอัลกอริทึมของ Dijkstra ...
Charles Stewart

อะไรคือวิธีที่ดีที่สุดในการติดตามตำแหน่งทั้งหมดที่เราเดินทางไปแล้วเพื่อให้ต้นไม้ BFS เติบโตไปข้างหน้าเท่านั้นและเมื่อเราค้นพบโหนดที่มีอยู่จากจุดใหม่เราจะไม่ต้องเพิ่มโหนดที่เก่ากว่าอีกครั้ง.. และการติดอยู่ใน วนไม่สิ้นสุด!
Nitish Upreti

ฉันคิดว่าเราสามารถทำได้โดยการเก็บตำแหน่งอัศวินคนสุดท้ายของเรา?
Nitish Upreti

7

วิธีแก้ไขจากหลักการแรกใน Python

ฉันพบปัญหานี้เป็นครั้งแรกในการทดสอบ Codility พวกเขาให้เวลาฉัน 30 นาทีในการแก้ปัญหา - ฉันใช้เวลานานกว่านั้นมากเพื่อให้ได้ผลลัพธ์นี้! ปัญหาคือ: อัศวินต้องใช้เวลากี่ท่าในการเปลี่ยนจาก 0,0 เป็น x, y โดยใช้ท่าของอัศวินตามกฎหมายเท่านั้น x และ y ไม่ถูกผูกมัดมากหรือน้อย (เราไม่ได้พูดถึงกระดานหมากรุก 8x8 แบบธรรมดา)

พวกเขาต้องการโซลูชัน O (1) ฉันต้องการวิธีแก้ปัญหาที่โปรแกรมสามารถแก้ปัญหาได้อย่างชัดเจน (เช่นฉันต้องการสิ่งที่ถูกต้องชัดเจนกว่ารูปแบบของ Graeme - รูปแบบมีนิสัยที่ทำลายลงโดยที่คุณไม่ได้มอง) และฉันไม่ต้องการที่จะพึ่งพา สูตรที่ไม่มีใครโต้แย้งเช่นเดียวกับในการแก้ปัญหาของมุสตาฟา

นี่คือทางออกของฉันสำหรับสิ่งที่คุ้มค่า เริ่มต้นอย่างที่คนอื่นสังเกตว่าวิธีแก้ปัญหานั้นสมมาตรเกี่ยวกับแกนและเส้นทแยงมุมดังนั้นเราจึงต้องแก้เฉพาะสำหรับ 0> = y> = x เพื่อความง่ายในการอธิบาย (และรหัส) ฉันจะย้อนกลับปัญหา: อัศวินเริ่มต้นที่ x, y และตั้งเป้าที่ 0,0

สมมติว่าเราย่อขนาดปัญหาให้อยู่ใกล้กับจุดเริ่มต้น เราจะไปถึงสิ่งที่ 'vicinty' หมายถึงในเวลาอันสมควร แต่สำหรับตอนนี้เรามาเขียนวิธีแก้ปัญหาใน cheatsheet (จุดเริ่มต้นที่ด้านล่างซ้าย):

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

ดังนั้นเมื่อกำหนด x, y บนกริดเราก็อ่านจำนวนการเคลื่อนที่ไปยังจุดเริ่มต้นได้

ถ้าเราเริ่มนอกเส้นตารางเราต้องกลับไปหามัน เราแนะนำ 'midline' ซึ่งเป็นเส้นที่แทนด้วย y = x / 2 อัศวินใด ๆ ที่ x, y ในบรรทัดนั้นสามารถกลับไปที่ cheatsheet ได้โดยใช้ชุดการเคลื่อนที่ 8 นาฬิกา (นั่นคือ: (-2, -1) การเคลื่อนไหว) ถ้า x, y อยู่เหนือเส้นกึ่งกลางเราจะต้องมีการเคลื่อนที่ 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 และ cheatsheet [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 นาฬิกาจากนั้นไปข้างหน้า 1 ทุ่ม

ดังนั้นเราต้องเพิ่มอีกหนึ่งหมายเลขลงใน cheatsheet:

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

อนึ่งหากคุณต้องการทราบเส้นทางที่แท้จริงอัลกอริทึมนี้ก็ให้สิ่งนั้นเช่นกันมันเป็นเพียงการต่อเนื่องของการเคลื่อนไหว 7 นาฬิกาของ n7 ตามด้วย (หรือสลับกับ) 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 + m ใด ๆ เช่นระยะทางนั้น (s + m) == d-1 ทำซ้ำจนกว่าเราจะไปถึงจุดกำเนิด

ฮาวแซท?


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 (ซอฟต์แวร์นองเลือด)


2

ฉันคิดว่าสิ่งนี้อาจช่วยคุณได้เช่นกัน ..

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

และใช้ Dynamic Programming เพื่อรับโซลูชัน

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

เพียงแค่รหัสทับทิมจากjsfiddle ของคำตอบของ Graeme Pyle ด้านบนลายรหัสพิเศษทั้งหมดและแปลงเป็นทับทิมเพื่อให้ได้วิธีแก้ปัญหาโดยอัลกอริทึมของเขาดูเหมือนจะใช้งานได้ ยังคงทดสอบแม้ว่า:

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
สามารถปรับให้เหมาะสมเพิ่มเติมเพื่อหลีกเลี่ยงการซ้ำกัน
อรุณ

-1

นี่คือเวอร์ชัน C ที่ใช้รหัส Mustafa Serdar Şanlıที่ใช้ได้กับบอร์ด 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.