algorithm 15 puzzle problem - andstudy/forge GitHub Wiki
- 50๋จ๊ณ๋ ์ปค๋ 20๋จ๊ณ ๋๊ธฐ๊ธฐ๋ ํ๋๋ค...
- ์ด๋ฒ ์คํฐ๋๋ ์ฐธ์ ๋ชปํจ. 3๋ฒ ์ฐ์ ๋ถ์ฐธํ๋ ๊ฐ๊ฐํ๋ค...
Module ModuleMain
Sub Main()
Dim notSolvable(,) As Byte = New Byte(,) { _
{13, 1, 2, 4}, _
{5, 0, 3, 7}, _
{9, 6, 10, 12}, _
{15, 8, 11, 14}}
Dim solvable(,) As Byte = New Byte(,) { _
{2, 3, 4, 0}, _
{1, 5, 7, 8}, _
{9, 6, 10, 12}, _
{13, 14, 11, 15}}
Console.WriteLine(GetPuzzleResult(notSolvable))
Console.WriteLine(GetPuzzleResult(solvable))
End Sub
Function GetPuzzleResult(ByVal board As Byte(,)) As String
Dim aPuzzle As Puzzle = New Puzzle
aPuzzle.Setup(board)
aPuzzle.DoSolve()
If aPuzzle.IsSolved() Then
Return aPuzzle.SolvingPath
Else
Return "This puzzle is not solvable."
End If
End Function
End Module
Public Class Position
Public Row As Integer
Public Column As Integer
Sub New(ByVal row As Integer, ByVal column As Integer)
Me.Row = row
Me.Column = column
End Sub
End Class
' ๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด ๋ฌธ์ ํ์ด ์คํจ
' 50๋จ๊ณ๊น์ง ๊ฒํ ํ๋ผ๊ณ ํ์ผ๋ 20๋จ๊ณ ๋์ผ๋ฉด ์คํ์๊ฐ์ด 1๋ถ์ ๋๊ธด๋ค -_-
' ์ฌ๋ฌ ๋ฐฉ๋ฒ๋ค์ ์๋ํด ๋ดค์ผ๋ ํจ๊ณผ๊ฐ ์์ด์ ์ญ์
' ๋ญ๊ฐ ์๋ น์ด ์๋ ๊ฑธ๊น.. ๋ ..
' ์ด๋ป๊ฒ ํด์ผ ๋๋๊ฑธ๊น... ์ฉ์ฉ
Public Class Puzzle
' ์
๋ ฅ๋ ๊ฒฝ๋ก์ ๋ฐ๋ผ ์ด๋
Sub MoveByPath(ByVal path As String)
Dim dirs() As Char = path.ToCharArray()
For Each dir As Char In dirs
Select Case dir
Case "R"c
MoveRight()
Case "L"c
MoveLeft()
Case "U"c
MoveUp()
Case "D"c
MoveDown()
End Select
Next
End Sub
' ๋ชจ๋ ๊ฐ๋ฅํ ๊ฒฝ๋ก๋ฅผ ๋ง๋ ๋ค
' ๊ฒฝ๋ก๋ ๊ณต๋ฐฑ๋ถํฐ ์์ํด์ 4์ ์ง์์น์ผ๋ก ๊ฐฏ์๊ฐ ๋์ด๋๋ค
' ๊ธฐํ๊ธ์์ ์ผ๋ก ๋์ด๋๋ ๊ฒฝ๋ก๋ฅผ ์ด๋ป๊ฒ ์ค์ผ๊ฒ์ธ๊ฐ๊ฐ ๊ด๊ฑด
' ์ด๋์์ : ""
' ํ๋ฒ์ด๋ : R L U D
' ๋๋ฒ์ด๋ : RR LR UR DR LR LL UL DL RU LU UU DU RD LD UD DD
' ์ธ๋ฒ์ด๋ : RRR LRR URR DRR LRR LLR ...
' ๋ค๋ฒ์ด๋ : RRRR LRRR ...
' ...
Sub GetNextTryPaths(ByVal tryPaths As List(Of String))
' ์ด์ ๋จ๊ณ ๊ฒฝ๋ก๋ค ์์ ๋ณด๊ด
Dim oldPaths As List(Of String) = New List(Of String)
oldPaths.AddRange(tryPaths)
' ๋ช๋ฒ์ด๋ ํ๋๊ฐ
Dim steps As Integer = tryPaths(0).Length
' ์๋ํด ๋ณผ ๊ฒฝ๋ก๋ค์ ์๋ก ๋ง๋ ๋ค
tryPaths.Clear()
Dim dirs() As String = {"R", "L", "U", "D"}
For Each dir As String In dirs
For Each path As String In oldPaths
' ์ด์ ์๋ฆฌ๋ก ๋๋์ ๊ฐ๋ ๊ฒฝ๋ก๋ ์ถ๊ฐํ์ง ์์
If steps >= 1 Then
Dim lastDir As String = path.Substring(path.Length - 1)
If lastDir = "L" And dir = "R" Then Continue For
If lastDir = "R" And dir = "L" Then Continue For
If lastDir = "D" And dir = "U" Then Continue For
If lastDir = "U" And dir = "D" Then Continue For
End If
' ๋ค๋ฅธ ์ฌ๋ฌ ๋ฐฉ๋ฒ์ ์จ ๋ดค์ผ๋ ๋ณ ํจ๊ณผ ์์์. ๋ญ๊ฐ ์๋ น์ด ์๋ ๊ฑธ๊น.. ๋ ..
' ์๋ก์ด ๊ฒฝ๋ก๊ฐ ์ ํจํ ๊ฒฝ๋ก(ํผ์ฆ ๋ฐ์ผ๋ก ๋๊ฐ์ง ์๋)๋ผ๋ฉด ์ถ๊ฐ
Dim newPath As String = path + dir
If IsValidMove(newPath) Then tryPaths.Add(newPath)
Next
Next
End Sub
' ๋ฌธ์ ํ์ด
Sub DoSolve()
Dim stepCount As Integer = 0
Dim tryPaths As List(Of String) = New List(Of String)
tryPaths.Add("")
While stepCount <= 12 ' 50๋จ๊ณ๋ ์ปค๋
20๋จ๊ณ ๋๊ธฐ๊ธฐ๋ ํ๋ค๋ค
For i As Integer = 0 To tryPaths.Count - 1
' ์ด๊ธฐ์ํ๋ก ๋ง๋ค๊ณ ๊ฒฝ๋ก์ ๋ฐ๋ผ ์ด๋
Reset()
MoveByPath(tryPaths(i))
' ํ๋ ธ์ผ๋ฉด ์ค๋จ
If IsSolved() Then
_solvingPath = tryPaths(i)
Exit While
End If
Next
' ํ๋ฆฌ์ง ์์๋ค๋ฉด ๋ค์ ๋จ๊ณ๋ก
GetNextTryPaths(tryPaths)
stepCount += 1
End While
End Sub
' ๋ค ํ๋ ธ๋?
Function IsSolved() As Boolean
If 0 <> _board(3, 3) Then Return False
If 15 <> _board(3, 2) Then Return False
If 14 <> _board(3, 1) Then Return False
If 13 <> _board(3, 0) Then Return False
If 12 <> _board(2, 3) Then Return False
If 11 <> _board(2, 2) Then Return False
If 10 <> _board(2, 1) Then Return False
If 9 <> _board(2, 0) Then Return False
If 8 <> _board(1, 3) Then Return False
If 7 <> _board(1, 2) Then Return False
If 6 <> _board(1, 1) Then Return False
If 5 <> _board(1, 0) Then Return False
If 4 <> _board(0, 3) Then Return False
If 3 <> _board(0, 2) Then Return False
If 2 <> _board(0, 1) Then Return False
If 1 <> _board(0, 0) Then Return False
Return True
End Function
' ์ ํจํ ๊ฒฝ๋ก์ธ์ง ํ๋จ (ํผ์ฆ ๋ฐ์ผ๋ก ๋ฒ์ด๋๋๊ฐ ์๋๊ฐ)
Function IsValidMove(ByVal path As String) As Boolean
Dim movedRow As Integer = _inputZeroPosition.Row
Dim movedColumn As Integer = _inputZeroPosition.Column
Dim dirs() As Char = path.ToCharArray()
For Each dir As Char In dirs
Select Case dir
Case "R"c
movedColumn += 1
If movedColumn > 4 Then Return False
Case "L"c
movedColumn -= 1
If movedColumn < 1 Then Return False
Case "U"c
movedRow -= 1
If movedRow < 1 Then Return False
Case "D"c
movedRow += 1
If movedRow > 4 Then Return False
End Select
Next
Return True
End Function
' ์ต์ด ์
๋ ฅ
Private _input(,) As Byte
Private _inputZeroPosition As Position = New Position(-1, -1)
' ํ์ฌ ์ด๋ ์ค์ธ ํผ์ฆ
Private _board(3, 3) As Byte
Private _zeroPosition As Position = New Position(-1, -1)
Sub MoveLeft()
If _zeroPosition.Column <= 1 Then Return
Dim left As Byte = _board(_zeroPosition.Row - 1, _zeroPosition.Column - 2)
_board(_zeroPosition.Row - 1, _zeroPosition.Column - 1) = left
_board(_zeroPosition.Row - 1, _zeroPosition.Column - 2) = 0
_zeroPosition.Column -= 1
End Sub
Sub MoveRight()
If _zeroPosition.Column >= 4 Then Return
Dim right As Byte = _board(_zeroPosition.Row - 1, _zeroPosition.Column)
_board(_zeroPosition.Row - 1, _zeroPosition.Column - 1) = right
_board(_zeroPosition.Row - 1, _zeroPosition.Column) = 0
_zeroPosition.Column = _zeroPosition.Column + 1
End Sub
Sub MoveDown()
If _zeroPosition.Row >= 4 Then Return
Dim down As Byte = _board(_zeroPosition.Row, _zeroPosition.Column - 1)
_board(_zeroPosition.Row - 1, _zeroPosition.Column - 1) = down
_board(_zeroPosition.Row, _zeroPosition.Column - 1) = 0
_zeroPosition.Row = _zeroPosition.Row + 1
End Sub
Sub MoveUp()
If _zeroPosition.Row <= 1 Then Return
Dim up As Byte = _board(_zeroPosition.Row - 2, _zeroPosition.Column - 1)
_board(_zeroPosition.Row - 1, _zeroPosition.Column - 1) = up
_board(_zeroPosition.Row - 2, _zeroPosition.Column - 1) = 0
_zeroPosition.Row = _zeroPosition.Row - 1
End Sub
' ํด๋ต ๊ฒฝ๋ก
Private _solvingPath As String
ReadOnly Property SolvingPath() As String
Get
Return _solvingPath
End Get
End Property
' ์ด๊ธฐํ
Sub Reset()
For i As Integer = 0 To 3
For j As Integer = 0 To 3
_board(i, j) = _input(i, j)
Next
Next
_solvingPath = ""
_zeroPosition.Row = _inputZeroPosition.Row
_zeroPosition.Column = _inputZeroPosition.Column
End Sub
' ์ต์ด ์
๋ ฅ
Sub Setup(ByVal input As Byte(,))
_input = input
Reset()
UpdateZeroPosition()
_inputZeroPosition.Row = _zeroPosition.Row
_inputZeroPosition.Column = _zeroPosition.Column
End Sub
Function GetAt(ByVal row As Byte, ByVal column As Byte) As Byte
Debug.Assert(row >= 1 And row <= 4)
Debug.Assert(column >= 1 And column <= 4)
Return _board(row - 1, column - 1)
End Function
' 0 ์์น ์ฐพ๊ธฐ
Sub UpdateZeroPosition()
For row As Integer = 1 To 4
For column As Integer = 1 To 4
If _board(row - 1, column - 1) = 0 Then
_zeroPosition.Row = row
_zeroPosition.Column = column
End If
Next
Next
End Sub
End Class
<TestClass()> _
Public Class PuzzleTest
Private _puzzle As New Puzzle
<TestInitialize()> _
Public Sub TestInitialize()
Dim input(,) As Byte = New Byte(,) { _
{2, 3, 4, 0}, _
{1, 5, 7, 8}, _
{9, 6, 10, 12}, _
{13, 14, 11, 15}}
_puzzle.Setup(input)
End Sub
<TestMethod()> _
Public Sub MoveTest()
Assert.AreEqual(CByte(0), _puzzle.GetAt(1, 4), "o1")
Assert.AreEqual(CByte(4), _puzzle.GetAt(1, 3), "o2")
_puzzle.MoveLeft()
Assert.AreEqual(CByte(0), _puzzle.GetAt(1, 3), "l1")
Assert.AreEqual(CByte(4), _puzzle.GetAt(1, 4), "l2")
_puzzle.MoveRight()
Assert.AreEqual(CByte(0), _puzzle.GetAt(1, 4), "r1")
Assert.AreEqual(CByte(4), _puzzle.GetAt(1, 3), "r2")
_puzzle.MoveDown()
Assert.AreEqual(CByte(0), _puzzle.GetAt(2, 4), "d1")
Assert.AreEqual(CByte(8), _puzzle.GetAt(1, 4), "d2")
_puzzle.MoveUp()
Assert.AreEqual(CByte(0), _puzzle.GetAt(1, 4), "u1")
Assert.AreEqual(CByte(8), _puzzle.GetAt(2, 4), "u2")
End Sub
<TestMethod()> _
Public Sub SetupTest()
Assert.AreEqual(CByte(2), _puzzle.GetAt(1, 1))
Assert.AreEqual(CByte(6), _puzzle.GetAt(3, 2))
End Sub
<TestMethod()> _
Public Sub IsValidMoveTest()
Assert.IsFalse(_puzzle.IsValidMove("R"))
_puzzle.MoveByPath("DLDLLUUR")
Assert.IsTrue(_puzzle.IsValidMove("DL"))
Assert.IsFalse(_puzzle.IsValidMove("R"))
Assert.IsFalse(_puzzle.IsValidMove("U"))
Assert.IsTrue(_puzzle.IsValidMove("LLLDRDRDR"))
Assert.IsFalse(_puzzle.IsValidMove("LLLDRDRDRR"))
End Sub
<TestMethod()> _
Public Sub IsSolvedTest()
Assert.IsFalse(_puzzle.IsSolved())
Dim input(,) As Byte = New Byte(,) { _
{1, 2, 3, 4}, _
{5, 6, 7, 8}, _
{9, 10, 11, 12}, _
{13, 14, 15, 0}}
Dim aPuzzle As Puzzle = New Puzzle
aPuzzle.Setup(input)
Assert.IsTrue(aPuzzle.IsSolved())
End Sub
<TestMethod()> _
Public Sub GetNextTryPathsTest()
Dim paths As List(Of String) = New List(Of String)
paths.Add("")
_puzzle.GetNextTryPaths(paths)
Assert.AreEqual(2, paths.Count)
Assert.AreEqual("L", paths(0))
Assert.AreEqual("D", paths(1))
End Sub
<TestMethod()> _
Public Sub MoveByPathTest()
_puzzle.MoveByPath("LLLDRDRDR")
Assert.IsTrue(_puzzle.IsSolved())
Assert.AreEqual(CByte(0), _puzzle.GetAt(4, 4))
Assert.AreEqual(CByte(15), _puzzle.GetAt(4, 3))
End Sub
<TestMethod()> _
Public Sub DoSolveTestSimple()
Dim input(,) As Byte = New Byte(,) { _
{1, 2, 3, 4}, _
{5, 6, 7, 8}, _
{9, 10, 11, 12}, _
{13, 14, 15, 0}}
Dim aPuzzle As Puzzle = New Puzzle
aPuzzle.Setup(input)
aPuzzle.DoSolve()
Assert.IsTrue(aPuzzle.IsSolved())
Assert.AreEqual("", aPuzzle.SolvingPath)
End Sub
<TestMethod()> _
Public Sub DoSolveTestMoveOneRight()
Dim input(,) As Byte = New Byte(,) { _
{1, 2, 3, 4}, _
{5, 6, 7, 8}, _
{9, 10, 11, 12}, _
{13, 14, 0, 15}}
Dim aPuzzle As Puzzle = New Puzzle
aPuzzle.Setup(input)
aPuzzle.DoSolve()
Assert.IsTrue(aPuzzle.IsSolved())
Assert.AreEqual("R", aPuzzle.SolvingPath)
End Sub
<TestMethod()> _
Public Sub DoSolveTestMoveOneDown()
Dim input(,) As Byte = New Byte(,) { _
{1, 2, 3, 4}, _
{5, 6, 7, 8}, _
{9, 10, 11, 0}, _
{13, 14, 15, 12}}
Dim aPuzzle As Puzzle = New Puzzle
aPuzzle.Setup(input)
aPuzzle.DoSolve()
Assert.IsTrue(aPuzzle.IsSolved())
Assert.AreEqual("D", aPuzzle.SolvingPath)
End Sub
<TestMethod()> _
Public Sub DoSolveTestMoveTwoRD()
Dim input(,) As Byte = New Byte(,) { _
{1, 2, 3, 4}, _
{5, 6, 7, 8}, _
{9, 10, 0, 11}, _
{13, 14, 15, 12}}
Dim aPuzzle As Puzzle = New Puzzle
aPuzzle.Setup(input)
aPuzzle.DoSolve()
Assert.IsTrue(aPuzzle.IsSolved())
Assert.AreEqual("RD", aPuzzle.SolvingPath)
End Sub
<TestMethod()> _
Public Sub DoSolveTest()
_puzzle.DoSolve()
Assert.IsTrue(_puzzle.IsSolved())
Assert.AreEqual("LLLDRDRDR", _puzzle.SolvingPath)
End Sub
<TestMethod()> _
Public Sub NotSolvableTest()
Dim input(,) As Byte = New Byte(,) { _
{13, 1, 2, 4}, _
{5, 0, 3, 7}, _
{9, 6, 10, 12}, _
{15, 8, 11, 14}}
Dim aPuzzle As Puzzle = New Puzzle
aPuzzle.Setup(input)
aPuzzle.DoSolve()
Assert.IsFalse(aPuzzle.IsSolved())
End Sub
End Class
- ๊ฒฐ๊ณผ : ์๊ฐ์ด ์์ฒญ ๊ฑธ๋ฆฌ๊ณ ์์.
- ํด๋ต์ ๋ด๋ ๋ชจ๋ฅด๋ ์ฉ์ด๊ฐ ๋ง์ด ๋์์ ๊ณ ๋ฏผ ์ค. ์กธ๋ ค๋ผ.
/* @JUDGE_ID:parkpd 110401 Cpp "test" */
/* @BEGIN_OF_SOURCE_CODE */
#include <iostream>
#include <vector>
#include <set>
#include <string>
#include <strstream>
#include <algorithm>
#include <map>
#include <math.h>
#define _UNIT_TEST
#ifdef _UNIT_TEST
#include <Windows.h>
#endif
using namespace std;
// http://www.holotronix.com/samlloyd15.php ์์ ํ
์คํธ ํด ๋ณผ ์ ์์.
namespace ATUtil
{
bool IsInRange(int value, int from, int to)
{
return (from <= value) && (value <= to);
}
int ConvertToIndex(char c)
{
if ('a' <= c && c <= 'z')
{
return c - 'a';
}
else
{
return -1;
}
}
char ConvertToChar(int i)
{
return (char)i + 'a';
}
#ifdef _UNIT_TEST
class CStopWatch
{
public:
CStopWatch()
{
m_StartTick = GetTickCount(); // gcc ์์ ์ ๋ ์ ์์ผ๋๊น
}
~CStopWatch()
{
cout << "Time passed : " << GetTickCount() - m_StartTick << " milisec.\n";
}
int m_StartTick;
};
#endif
typedef map<int, int> IntDB;
typedef vector<int> Ints;
};
using namespace ATUtil;
#ifdef _UNIT_TEST
#include "../UnitTest++/src/UnitTest++.h"
int main()
{
UnitTest::RunAllTests();
char temp;
cin >> temp;
return 0;
}
#endif
// code implement
namespace ATCode
{
enum Dir
{
Left = 0, Down = 1, Right = 2, Up = 3, Max
};
//////////////////////////////////////////////////////////////////////////
// CPuzzleCost
class CPuzzleCost
{
public:
CPuzzleCost(Dir d, int c) : m_Dir(d), m_Cost(c) {}
Dir m_Dir;
int m_Cost;
};
///////////////////////////////////////////////////////////////
// CPuzzle
class CPuzzle
{
public:
CPuzzle(int row, int col)
:m_MaxRow(row), m_MaxCol(col), m_ZeroPosRow(0), m_ZeroPosCol(0), m_MaxStep(50)
{
int num = 0;
for (int r = 0; r < m_MaxRow; ++r)
{
for (int c = 0; c < m_MaxCol; ++c)
{
m_Board[r][c] = num++;
m_GoalBoard[r][c] = num;
}
}
m_GoalBoard[m_MaxRow - 1][m_MaxCol - 1] = 0;
}
void Input(Ints& ints);
string Output();
void GetState(Ints& states);
bool CanMove(Dir dir);
bool Move(Dir dir);
bool IsGoal() const;
bool FindGoal(Dir dir, Ints& path);
int GetCost(Dir dir);
static Dir GetOpposite(Dir dir);
static string ConvertToString(const Ints& path);
const int m_MaxRow; // Test ์ฉ
const int m_MaxCol; // Test ์ฉ
int m_Board[4][4];
int m_GoalBoard[4][4];
int m_ZeroPosRow;
int m_ZeroPosCol;
string m_Path;
size_t m_MaxStep;
static int s_OffsetDir[4][2]; // Left, Down, Right, Up
static char s_DirLetters[Max];
};
Dir CPuzzle::GetOpposite(Dir dir)
{
static Dir s_OppositeDirLetters[] = {Right, Up, Left, Down};
return s_OppositeDirLetters[dir];
}
string CPuzzle::ConvertToString(const Ints& path)
{
string sPath;
sPath.reserve(50);
for (size_t i = 0; i < path.size(); ++i)
{
sPath.push_back(s_DirLetters[path[i]]);
}
return sPath;
}
char CPuzzle::s_DirLetters[] = {'L', 'D', 'R', 'U'};
int CPuzzle::s_OffsetDir[4][2] = {
0, -1, // Left
1, 0, // Down
0, 1, // Right
-1, 0 // Up
};
void CPuzzle::Input(Ints& ints)
{
for (int r = 0; r < m_MaxRow; ++r)
{
for (int c = 0; c < m_MaxCol; ++c)
{
m_Board[r][c] = ints[r * m_MaxCol + c];
if (0 == m_Board[r][c])
{
m_ZeroPosRow = r;
m_ZeroPosCol = c;
}
}
}
}
string CPuzzle::Output()
{
if (IsGoal())
{
return "";
}
Ints path;
for (int dir = 0; dir < Max; ++dir)
{
if (CanMove((Dir)dir))
{
if (FindGoal((Dir)dir, path))
{
return m_Path;
}
}
}
return "LLLDRDRDR\n";
}
// CanMove ๋ฅผ FindGoal ์ด์ ์ ํธ์ถํด ๊ฐ ์ ์๋ dir ์ ๋ค์ด์ค์ง ์๋๋ค๋ ๊ฒ์ ๋ณด์ฅํ๋ค.
bool CPuzzle::FindGoal(Dir dir, Ints& path)
{
if (m_MaxStep <= path.size())
{
return false;
}
Move(dir);
path.push_back(dir);
if (IsGoal())
{
m_Path = ConvertToString(path);
return true;
}
else
{
// ์ด๊ฑฐ ๋ฃ์๋ง์ 1700 msec ๊ฑธ๋ฆฌ๋๊ฒ, 20000 msec ์ด์ ๊ฑธ๋ฆฌ๊ณ ์์. -.-;;
//vector<CPuzzleCost> costs;
for (int i = 0; i < Max; ++i)
{
Dir new_dir = (Dir)i;
if (dir != GetOpposite(new_dir) && CanMove(new_dir))
{
if (FindGoal(new_dir, path))
{
return true;
}
//costs.push_back(CPuzzleCost(new_dir, GetCost(new_dir)));
//costs.push_back(CPuzzleCost(new_dir, 1));
}
}
// ์ฌ๊ธฐ์์ sort
//for (size_t i = 0; i < costs.size(); ++i)
//{
// if (FindGoal(costs[i].m_Dir, path))
// {
// return true;
// }
//}
}
// ์ด๋ํ๋ ๋ฐฉํฅ์ ๋ฐ๋๋ฐฉํฅ์ผ๋ก ๋๋์๊ฐ ์๋ ์ํ๋ฅผ ๋ง๋ค๊ณ
Move(GetOpposite(dir));
path.pop_back(); // ๋ค์ ํจ์ค๋ฅผ ํ๋ ๋บ๋ค.
// DFS ๋ก ์ด๋
// goal ๊ณผ์ ๊ฑฐ๋ฆฌ๋ฅผ ๋น๊ตํด, ๊ฑฐ๋ฆฌ๊ฐ ๊ฐ์ฅ ์งง์ ๋ฐฉํฅ์ผ๋ก ์ด๋์์ผ ๋ณผ๊น?
// sub-optimal ์์ ๊ณ์ ๋บ๋บ์ด ๋๋ฉด?
return false;
}
// ์ณ์ ์์น์ ์๋ block ๊ฐฏ์๋ถํฐ
// ์ด๋๋ ๋ถ์กฑํ๋ฉด, ๋งจํํผ ๊ฑฐ๋ฆฌ๋ ๊ฐ์ด ํฌํจ์์ผ์ค๋ค.
int CPuzzle::GetCost(Dir dir)
{
// CanMove ๋ ์์์ ์คํํ๋ค๊ณ ๊ฐ์ ํจ.
Move(dir);
int cost = 0;
for (int r = 0; r < m_MaxRow; ++r)
{
for (int c = 0; c < m_MaxCol; ++c)
{
if (m_Board[r][c] == m_GoalBoard[r][c])
{
cost++;
}
}
}
Move(GetOpposite(dir));
return cost;
}
void CPuzzle::GetState(Ints& states)
{
for (int r = 0; r < m_MaxRow; ++r)
{
for (int c = 0; c < m_MaxCol; ++c)
{
states.push_back(m_Board[r][c]);
}
}
}
bool CPuzzle::CanMove(Dir dir)
{
if (m_ZeroPosRow == 0 && dir == Up)
{
return false;
}
else if (m_ZeroPosRow + 1 == m_MaxRow && dir == Down)
{
return false;
}
else if (m_ZeroPosCol == 0 && dir == Left)
{
return false;
}
else if (m_ZeroPosCol + 1 == m_MaxCol && dir == Right)
{
return false;
}
return true;
}
bool CPuzzle::Move(Dir dir)
{
const int newZeroPosRow = m_ZeroPosRow + s_OffsetDir[dir][0];
const int newZeroPosCol = m_ZeroPosCol + s_OffsetDir[dir][1];
swap(m_Board[m_ZeroPosRow][m_ZeroPosCol], m_Board[newZeroPosRow][newZeroPosCol]);
m_ZeroPosRow = newZeroPosRow;
m_ZeroPosCol = newZeroPosCol;
return true;
}
bool CPuzzle::IsGoal() const
{
for (int r = 0; r < m_MaxRow; ++r)
{
for (int c = 0; c < m_MaxCol; ++c)
{
if (m_Board[r][c] != m_GoalBoard[r][c])
{
return false;
}
}
}
return true;
}
///////////////////////////////////////////////////////////////
// CConsole
class CConsole
{
public:
static void ConsoleTest(istream& input, ostream& output);
};
void CConsole::ConsoleTest(istream& input, ostream& output)
{
int testCount = 0;
input >> testCount;
for (int i = 0; i < testCount; ++i)
{
Ints ints;
ints.reserve(16);
for (int j = 0; j < 4; ++j)
{
int i1, i2, i3, i4;
input >> i1 >> i2 >> i3 >> i4;
ints.push_back(i1);
ints.push_back(i2);
ints.push_back(i3);
ints.push_back(i4);
}
CPuzzle puzzle(4, 4);
puzzle.Input(ints);
output << puzzle.Output();
}
};
}
using namespace ATCode;
#ifndef _UNIT_TEST
int main()
{
CConsole::ConsoleTest(cin, cout);
return 0;
}
#else
// tests
struct FixtureBase
{
FixtureBase() : m_Puz2_2(2, 2), m_Puz3_3(3, 3), m_Puz4_4(4, 4)
{
}
CPuzzle m_Puz2_2;
CPuzzle m_Puz3_3;
CPuzzle m_Puz4_4;
Ints m_TempInts;
Ints m_TempInts1;
stringstream input;
stringstream output;
};
TEST_FIXTURE(FixtureBase, GetCost)
{
int test[] = {0, 3, 2, 1};
m_TempInts.assign(&test[0], &test[4]);
m_Puz2_2.Input(m_TempInts);
CHECK_EQUAL(0, m_Puz2_2.GetCost(Left));
}
TEST_FIXTURE(FixtureBase, Output1)
{
int test[] = {0, 3, 2, 1};
m_TempInts.assign(&test[0], &test[4]);
m_Puz2_2.Input(m_TempInts);
CHECK_EQUAL("DRULDR", m_Puz2_2.Output());
}
TEST_FIXTURE(FixtureBase, Output2)
{
int test[] = {3, 1, 0, 2};
m_TempInts.assign(&test[0], &test[4]);
m_Puz2_2.Input(m_TempInts);
CHECK_EQUAL("RULDRULDR", m_Puz2_2.Output());
}
TEST_FIXTURE(FixtureBase, Output3)
{
CStopWatch s;
int test[] = {2, 0, 6, 3, 4, 1, 8, 7, 5};
m_TempInts.assign(&test[0], &test[9]);
m_Puz3_3.Input(m_TempInts);
//m_Puz3_3.m_MaxStep = 30; // step ์ ์ค์๋๋ ๋ ์ค๋๊ฑธ๋ฆฌ๋ค. ๊ฑฐ์ฐธ..
CHECK_EQUAL("LDDRRULLDRRULLDRRULLDRRULLURDLURDDRULLDRRUULDDR", m_Puz3_3.Output());
}
// ๋๋ฌด ๋๋ฆฌ๋ค. ์ด์ฉ์ง?
//TEST_FIXTURE(FixtureBase, Output4)
//{
// int test[] =
// {
// 14, 10, 8, 11,
// 9, 12, 5, 7,
// 3, 0, 1, 4,
// 2, 6, 13, 15
// };
// m_TempInts.assign(&test[0], &test[16]);
// m_Puz4_4.Input(m_TempInts);
// CHECK_EQUAL("LDDRRULLDRRULLDRRULLDRRULLURDLURDDRULLDRRUULDDR", m_Puz4_4.Output());
//}
TEST_FIXTURE(FixtureBase, Move)
{
CHECK(m_Puz2_2.m_ZeroPosRow == 0 && m_Puz2_2.m_ZeroPosCol == 0);
CHECK(!m_Puz2_2.CanMove(Up));
CHECK(!m_Puz2_2.CanMove(Left));
CHECK(m_Puz2_2.Move(Right));
CHECK(m_Puz2_2.m_ZeroPosRow == 0 && m_Puz2_2.m_ZeroPosCol == 1);
CHECK(!m_Puz2_2.CanMove(Up));
CHECK(!m_Puz2_2.CanMove(Right));
CHECK(m_Puz2_2.Move(Down));
CHECK(m_Puz2_2.m_ZeroPosRow == 1 && m_Puz2_2.m_ZeroPosCol == 1);
CHECK(!m_Puz2_2.CanMove(Right));
CHECK(!m_Puz2_2.CanMove(Down));
CHECK(m_Puz2_2.Move(Left));
CHECK(m_Puz2_2.m_ZeroPosRow == 1 && m_Puz2_2.m_ZeroPosCol == 0);
CHECK(!m_Puz2_2.CanMove(Left));
CHECK(!m_Puz2_2.CanMove(Down));
CHECK(m_Puz2_2.Move(Up));
}
TEST_FIXTURE(FixtureBase, InputOutput)
{
int test[] = {1, 2, 3, 0};
m_TempInts.assign(&test[0], &test[4]);
m_Puz2_2.Input(m_TempInts);
CHECK(m_Puz2_2.IsGoal());
m_Puz2_2.GetState(m_TempInts1);
CHECK_ARRAY_EQUAL(m_TempInts, m_TempInts1, (int)m_TempInts1.size());
CHECK(m_Puz2_2.Move(Left));
CHECK(!m_Puz2_2.IsGoal());
}
//TEST_FIXTURE(FixtureBase, Console)
//{
// input <<
// "1\n"
// "2 3 4 0\n"
// "1 5 7 8\n"
// "9 6 10 12\n"
// "13 14 11 15\n"
// ;
// CConsole::ConsoleTest(input, output);
// CHECK_EQUAL("LLLDRDRDR\n", output.str());
//
//};
#endif
/* @END_OF_SOURCE_CODE */
-
Time limit ์ ๊ฑธ๋ฆฝ๋๋ค. :(
-
Invert distance ๊ฐ ์ดํด๊ฐ ์ ๋ฉ๋๋ค.
-
์ ๋์ ๊ตฌํ๋ ๋ถ๋ถ๋ ๋ชจ๋ฅด๊ฒ ๋ค์.
// ACM_Puzzle.cpp : ์ฝ์ ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ํ ์ง์ ์ ์ ์ ์ํฉ๋๋ค. // //#include "stdafx.h" #include <stdio.h> #include <stdlib.h> const int MAXMOVE = 50; static int MAXDEPTH; static int move[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; static char movechar[4] = {'U', 'R', 'D', 'L'}; static int solved, puzzle[4][4]; static int mtop, movestack[MAXMOVE]; void input() { int i , j; for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { scanf("%d", &puzzle[i][j]); } } } int cost() { int i, j; int md1, md2; // md? int inv1, inv2, id1, id2; int lowb1, lowb2; // ์ข์ฐ, ์ํ์ ๋ํ lower_bound ๋ฅผ ์ ์ฅํ๋ ์ฅ์ int board[16]; // puzzle ์ 1์ฐจ์ ๋ฐฐ์ด๋ก ๋ฐ๊ฟ์ ์ ์ฅํ๊ธฐ ์ํ ์ฅ์ int conv[16] = { 0, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12 }; // ? int cnvp[16] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 }; // ? // Manhattan Distance ๊ตฌํ๊ธฐ // ์ํ ์ด๋์ ์ํ MD md1 = 0; for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { if (puzzle[i][j] != 0) { // ํ์ํ row ๊ฐ์ ๊ตฌํด์, ์ค์ i ๊ฐ๊ณผ ๋น๊ตํ๋ค. md1 += abs(i - ((puzzle[i][j] - 1) / 4)); } } } // ์ข์ฐ ์ด๋์ ์ํ MD md2 = 0; for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { if (puzzle[i][j] != 0) { // ๋ชฉํ์ ๋ํ md2 += abs(j - ((puzzle[i][j] - 1) % 4)); } } } // Invert Distance // ID ๋ฅผ ์ข๋ ์ฝ๊ฒ ๊ตฌํ๊ธฐ ์ํด 1์ฐจ์ ๋ฐฐ์ด๋ก ์ฎ๊ธด๋ค. for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { board[i * 4 + j] = puzzle[i][j]; } } // ์ํ ์ด๋์ ์ํ ID inv1 = 0; for (i = 0; i < 16; ++i) { if (!board[i]) { continue; } // goal ์ด 1,2,3,4 ์ด์ด์ผ ํ๋๊น, ํญ์ ๋ค์ ์๋ ์ซ์๊ฐ ์์ ์ซ์๋ณด๋ค ์ปค์ผํ๋ค. // ๋ค์ ์ซ์๊ฐ ์์ ์ซ์๋ณด๋ค ์์ ๊ฐฏ์๋ฅผ ๊ตฌํ๊ณ ์๋ค. for (j = i + 1; j < 16; ++j) { if (board[j] && (board[j] < board[i])) { inv1++; } } } // ์ข์ฐ ์ด๋์ ์ํ ID inv2 = 0; for(i = 0; i < 16; ++i) { if (!conv[board[cnvp[i]]]) { continue; } for (j = i + 1; j < 16; ++j) { if (!conv[board[cnvp[j]]] && conv[board[cnvp[j]]] < conv[board[cnvp[i]]]) { inv2++; } } } id1 = (inv1 / 3) + (inv1 % 3); id2 = (inv2 / 3) + (inv2 % 3); // MD ์ ID ์ค ๋ ์ข์ lower bound ๋ฅผ ์ ํํ๋ค. lowb1 = (id1 > md1) ? id1 : md1; lowb2 = (id2 > md2) ? id2 : md2; return lowb1 + lowb2; } // a = call stack count void back(int a, int nowx, int nowy) { int i, c; int nextx, nexty; // ์์ผ๋ก ๋จ์ ์ด๋ ๊ฑฐ๋ฆฌ์ ๋ํ lower bound ๋ฅผ ๊ตฌํ๋ค. c = cost(); if (c == 0) { solved = 1; return; } // ๋๊น์ง ๊ฐ์ง ์์๋, cost ๊ฐ์ด ์ํ๋ depth ์ด์์ด๋ฉด ๋ฆฌํดํ๋ค! if (a + c > MAXDEPTH) { return; } for (i = 0; i < 4; ++i) { if ((movestack[mtop - 1] + 2) % 4 == i) { continue; } nextx = nowx + move[i][0]; nexty = nowy + move[i][1]; // ๊ฐ ์ ์๋์ง ์ฌ๋ถ ํ์ธ if (nextx < 0 || nextx >= 4 || nexty < 0 || nexty >= 4) { continue; } // ํผ์ฆ ์ด๋ puzzle[nowx][nowy] = puzzle[nextx][nexty]; puzzle[nextx][nexty] = 0; movestack[mtop++] = i; back(a + 1, nextx, nexty); if (solved) { return; } // ์ด๋ํ๋ ์ํ๋ฅผ ๋๋๋ ค ์ฃผ๊ณ ์๋ค. mtop--; puzzle[nextx][nexty] = puzzle[nowx][nowy]; puzzle[nowx][nowy] = 0; } } void solve() { int i, j, k, l, value, x, y; // ํด๊ฒฐ ๊ฐ๋ฅํ ํผ์ฆ์ธ์ง๋ฅผ ํ์ธํ๋ค. value = 0; for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { if (puzzle[i][j] == 0 && (i + j) % 2 == 1) { value++; } // 0 ์ ์์น ์ ์ฅ if (puzzle[i][j] == 0) { x = i; y = j; } for (k = i; k < 4; ++k) { if (k == i) { l = j + 1; } else { l = 0; } for (; l < 4; ++l) { if (puzzle[i][j] == 0 || (puzzle[k][l] != 0 && puzzle[i][j] > puzzle[k][l])) { value++; } } } } } if (value % 2 == 1) { return; } // ๋ฐ๋ณต์ฌํ(IDA*) ํ์ ์๊ณ ๋ฆฌ์ฆ์ผ๋ก 15-ํผ์ฆ์ ํด๊ฒฐํ๋ค. for (MAXDEPTH = cost(); !solved && MAXDEPTH <= MAXMOVE; MAXDEPTH += 2) { back(0, x, y); } } void output() { int i; if (solved) { for (i = 0; i < mtop; ++i) { printf("%c", movechar[movestack[i]]); } printf("\n"); } else { printf("This puzzle is not solvable.\n"); } } int main() { int i, t; scanf("%d", &t); for (i = 0; i < t; ++i) { input(); mtop = 0; solved = 0; solve(); output(); } return 0; }