|
| 1 | +ข้อนี้ให้ค่าอุณหภูมิในตาราง $N \times N$ $(N \leq 20)$ และกำหนดว่าจากช่องใดๆ จะสามารถขยับไปช่องรอบๆ ที่มีอุณหภูมิที่สูงกว่าเท่านั้น โดยมีบางช่องที่ค่าอุณหภูมิเป็น 100 ซึ่งจะเข้าไม่ได้เลย |
| 2 | + |
| 3 | +### แนวคิด |
| 4 | + |
| 5 | +สำหรับข้อนี้เราสามารถมองว่าแต่ละช่องของเป็นตารางที่ไม่ได้มีค่าอุณหภูมิเป็น 100 เป็น Node และระหว่างสองช่องที่ติดกันจะมี Edge ที่มีทิศทางจากช่องที่มีอุณหภูมิน้อยกว่าไปยังช่องที่มีอุณหภูมิมากกว่า |
| 6 | + |
| 7 | +จากนั้นหากทำ State Space Search เพื่อหาว่า Node ใดในตารางบ้างที่มี Path ที่เป็นไปได้จากช่องเริ่มต้นและเอาอันที่มีอุณหภูมิสูงสุดจะได้คำตอบที่ต้องการ |
| 8 | + |
| 9 | +เช่นในตัวอย่างประกอบในโจทย์สามารถวาด Edge ได้ดังในรูป |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +### State Space Search |
| 14 | + |
| 15 | +State Space Search (การค้นหาในปริภูมิสถานะ) เป็นรูปแบบขั้นตอนวิธีที่จะแทน State (สถานะ) เป็น Node ใน Graph โดย Node $A$ จะมี Edge ไปยัง Node $B$ ก็ต่อเมื่อสามารถเปลี่ยนจาก State $A$ ไป $B$ ได้ในหนึ่งขั้น ใน State Space Search เราจะเริ่มจาก Node ใดๆ แล้วพิจารณา Node รอบข้างที่มี Edge เชื่อมเพื่อพิจารณาแต่ละ State ที่สามารถไปถึงจาก State เริ่ม สำหรับข้อนี้จะต้องหา State ที่มีค่าอุณหภูมิสูงสุด |
| 16 | + |
| 17 | +รูปประกอบต่อไปนี้เป็นตัวอย่างของ State Space |
| 18 | + |
| 19 | + |
| 20 | + |
| 21 | +ในตัวอย่างนี้จะเห็นได้ว่า State $B$ สามารถไป $D$ และ $D$ กับ $E$ สามารถไปมาซึ่งกันและกัน |
| 22 | + |
| 23 | +ในการทำ State Space Search จะมีวิธีพื้นฐานหลักๆ สองแบบคือ Depth First Search (DFS) กับ Breadth First Search (BFS) |
| 24 | + |
| 25 | +#### DFS |
| 26 | + |
| 27 | +ใน DFS จะทำการค้นหาในรูปแบบ Recursive โดยสำหรับแต่ละ State ที่พิจารณา จะ DFS ลงไปใน Node ที่มี Edge ที่ยังได้ไม่ถูกพิจารณา |
| 28 | + |
| 29 | +หากทำ DFS จาก State $A$ ในตัวอย่างก่อนหน้านี้จะได้ดังในรูป (ลูกศรสีเขียวแทนการเรียก DFS แบบ Recursive และลูกศรสีแดงแทนการ Return กลับมา) |
| 30 | + |
| 31 | + |
| 32 | + |
| 33 | +ในตัวอย่างนี้จะพิจารณา State ในลำดับ $A,B,D,E,C,F,G$ |
| 34 | + |
| 35 | +เมื่อนำมาใช้กับข้อนี้จะได้โค้ดดังนี้ |
| 36 | + |
| 37 | +```cpp |
| 38 | +int M; |
| 39 | +int T[22][22]; |
| 40 | +int visited[22][22]; |
| 41 | + |
| 42 | +int dfs(int x, int y) { |
| 43 | + visited[x][y] = 1; // visited[x][y] จะแทนว่าช่อง x,y ถูกพิจารณาแล้ว |
| 44 | + |
| 45 | + int ret = T[x][y]; |
| 46 | + |
| 47 | + int dx[4] = {0, 0, -1, 1}; |
| 48 | + int dy[4] = {1, -1, 0, 0}; |
| 49 | + for (int u = 0; u < 4; u++) { // พิจารณา 4 ทิศทาง |
| 50 | + int ux = x + dx[u]; |
| 51 | + int uy = y + dy[u]; |
| 52 | + |
| 53 | + if (1 <= ux && ux <= M && 1 <= uy && uy <= M && !visited[ux][uy] && |
| 54 | + T[ux][uy] != 100 && |
| 55 | + T[ux][uy] > T[x][y]) // ช่องใหม่ต้องอยู่ในกรอบ ยังไม่ถูกพิจารณา และ |
| 56 | + // ต้องมีอุณหภูมิที่มากกว่าช่องปัจจุบันและไม่ใช่ 100 |
| 57 | + ret = max(dfs(ux, uy), ret); // ค่าที่มากที่สุดจะเป็นค่าที่มากสุดของช่องนี้หรือช่องที่สามารถไปจากช่องนี้ |
| 58 | + } |
| 59 | + |
| 60 | + return ret; |
| 61 | +} |
| 62 | +``` |
| 63 | +
|
| 64 | +ในโค้ดตัวอย่างนี้จะพิจารณาแต่ละช่องที่อยู่ด้าน บน ล่าง ซ้าย หรือ ขวา ว่าเข้าเงื่อนไขที่จะสามารถไปจากช่องปัจจุบันไหมและหากสามารถไปจะเรียก DFS แบบ Recursive |
| 65 | +
|
| 66 | +DFS จะใช้ Memory ใน Stack ตามความยาวของ Path ที่ทำ Recursion ซึ่งสำหรับข้อนี้เป็นได้อย่างมาก $\mathcal{O}(M^2)$ และจะใช้ Time Complexity ตามจำนวน Node และ Edge ที่ถูกพิจารณาซึ่งสำหรับข้อนี้เป็น $\mathcal{O}(M^2)$ เช่นกัน |
| 67 | +
|
| 68 | +#### BFS |
| 69 | +
|
| 70 | +สำหรับ BFS จะต่างจาก DFS ตรงที่เมื่อเจอ Edge ที่ไปได้แล้วจะไม่นำมาพิจารณาทันทีแต่ละนำเข้า Queue ก่อน โดยการพิจารณาแต่ละ Node จะเรียงตามลำดับใน Queue |
| 71 | +
|
| 72 | +ตัวอย่างดังภาพ |
| 73 | +
|
| 74 | + |
| 75 | +
|
| 76 | +ในตัวอย่างนี้จะพิจารณาเป็นลำดับ $A,B,C,D,E,F,G$ ตามลูกศรสีเขียว |
| 77 | +
|
| 78 | +โค้ด BFS ตัวอย่างสำหรับข้อนี้ |
| 79 | +
|
| 80 | +```cpp |
| 81 | +int M; |
| 82 | +int T[22][22]; |
| 83 | +
|
| 84 | +int bfs(int x, int y) { |
| 85 | + int queued[22][22]; |
| 86 | + queued[x][y] = 1; |
| 87 | + std::deque<std::pair<int, int>> q; // Queue สำหรับการเก็บแต่ละ Node |
| 88 | +
|
| 89 | + q.push_back(std::make_pair(x, y)); // นำ Node เริ่มต้นเข้า Queue |
| 90 | +
|
| 91 | + int ret = T[x][y]; |
| 92 | +
|
| 93 | + while (!q.empty()) { |
| 94 | + int qx = q[0].first, qy = q[0].second; |
| 95 | + q.pop_front(); // นำ Node แรกมาพิจารณาและเอาออกจาก Queue |
| 96 | +
|
| 97 | + ret = std::max(ret, T[qx][qy]); |
| 98 | +
|
| 99 | + int dx[4] = {0, 0, -1, 1}; |
| 100 | + int dy[4] = {1, -1, 0, 0}; |
| 101 | + for (int u = 0; u < 4; u++) { |
| 102 | + int ux = qx + dx[u]; |
| 103 | + int uy = qy + dy[u]; |
| 104 | +
|
| 105 | + if (1 <= ux && ux <= M && 1 <= uy && uy <= M && !queued[ux][uy] && |
| 106 | + T[ux][uy] != 100 && T[ux][uy] > T[qx][qy]) { |
| 107 | + q.push_back(std::make_pair(ux, uy)); // ใส่ Node ใหม่ที่เจอเข้า Queue |
| 108 | + queued[ux][uy] = 1; |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | +
|
| 113 | + return ret; |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +สังเกตว่าในโค้ดนี้จะเรียก BFS เพียงรอบเดียวต่างจาก DFS โดยจะนำ Node มาใส่ Queue แทนการใช้ Recursion |
| 118 | + |
| 119 | +BFS จะใช้ Memory ตามจำนวน Node ที่อยู่ใน Queue และเวลาตามจำนวน Node / Edge ที่ถูกพิจารณา |
| 120 | + |
| 121 | +ดังนั้นทั้งคู่จะเป็น $\mathcal{O}(M^2)$ สำหรับข้อนี้ |
0 commit comments