백준 17825 주사위 윷놀이 C++
2023. 10. 9. 18:45ㆍ알고리즘문제 풀이/백준
문제
주사위 윷놀이는 다음과 같은 게임판에서 하는 게임이다.
https://upload.acmicpc.net/43409ac6-54bf-4a21-b542-e01a8211e59f/-/preview/
- 처음에는 시작 칸에 말 4개가 있다.
- 말은 게임판에 그려진 화살표의 방향대로만 이동할 수 있다. 말이 파란색 칸에서 이동을 시작하면 파란색 화살표를 타야 하고, 이동하는 도중이거나 파란색이 아닌 칸에서 이동을 시작하면 빨간색 화살표를 타야 한다. 말이 도착 칸으로 이동하면 주사위에 나온 수와 관계 없이 이동을 마친다.
- 게임은 10개의 턴으로 이루어진다. 매 턴마다 1부터 5까지 한 면에 하나씩 적혀있는 5면체 주사위를 굴리고, 도착 칸에 있지 않은 말을 하나 골라 주사위에 나온 수만큼 이동시킨다.
- 말이 이동을 마치는 칸에 다른 말이 있으면 그 말은 고를 수 없다. 단, 이동을 마치는 칸이 도착 칸이면 고를 수 있다.
- 말이 이동을 마칠 때마다 칸에 적혀있는 수가 점수에 추가된다.
주사위에서 나올 수 10개를 미리 알고 있을 때, 얻을 수 있는 점수의 최댓값을 구해보자.
입력
첫째 줄에 주사위에서 나올 수 10개가 순서대로 주어진다.
출력
얻을 수 있는 점수의 최댓값을 출력한다.
풀이
1. 게임판의 구조를 잘 파악하고, 말의 현재 위치를 나타내는 변수를 관리한다.
struct STATE: 움직임에 대한 정보를 받아오는 구조체
struct YUT: 윷 구조체
STATE GetState(int Idx, int C_Idx): 현재 움직임으로 변하는 윷의 상태를 받아오는 함수
2. 모든 경우의 수를 탐색하면서 주사위를 굴려 말을 이동시킨다.
DFS(): 깊이 우선탐색
MakeState(): 방문 체크를 하고 실제로 윷을 옮기는 함수
3. 각 경우에서 모든 말의 점수 합을 계산하고 최대값을 찾는다.
DFS()함수 안에서 계산 과정이 이루어진다.
4. 최대 점수를 출력한다.
Answer에 최대 점수를 출력한다.
#include<iostream>
#define endl "\n"
#define MAX 10
using namespace std;
struct STATE // 움직임에 대한 정보를 받아오는 구조체
{
int Prev; // 현재 칸
int Next; // 이동하려는 칸
int Start_Circle; // 시작한 파랑원의 번호(1, 2, 3 중 하나)
bool Select_Circle; // 이번 움직임으로 파랑색 원의 번호가 결정되었는지에 대한 여부판단
bool Finish; // 이번 움직임으로 윷이 도착점에 도달하였는지에 대한 여부판단
};
struct YUT
{
int Blue_Circle; // 윷이 한번이라도 시작한 파랑색 원의 번호
int Pos; // 윷의 정보
int Score; // 윷의 점수
bool Finish; // 윷이 도착점에 도달했는지에 대한 여부
};
int Answer;
int Command[MAX];
int MoveNum[4]; // 각 경로 별, 움직여야 하는 최대 칸수를 저장하는 배열
int MAP_Score[4][30]; // 점수 판
bool Visit[4][30]; // 이미 다른 윷이 있는지 없는지 판단하기 위한 배열
bool Special_Point[4][30]; // 특별한 점 (모든 경로가 겹치는 칸들)
YUT Yut[4];
STATE GetState(int Idx, int C_Idx){
/* 현재의 움직임으로 변하는 윷의 상태를 받아오는 함수 */
int Prev = Yut[Idx].Pos; // 윷이 현재 있는 칸
int Next = Yut[Idx].Pos + Command[C_Idx]; // 윷이 이동하려는 칸
int Start_Circle = Yut[Idx].Blue_Circle; // 기존에 시작한 파랑색 원의 번호
bool Select_Circle = false; // 이번 턴의 움직임으로 인해 파랑색 원이 결정되었는지에 대한 여부
bool Finish = false; // 이번 턴의 움직임으로 인해 도착점에 도달했는지에 대한 여부
if (Start_Circle == 0){ // 아직 시작한 파랑색 원이 없을 경우에
if (Prev == 5 || Prev == 10 || Prev == 15){ // 파랑색 원에서 시작한다
Select_Circle = true; // 이번 턴의 움직임으로 파랑색 원이 결정되었다.
Start_Circle = Prev / 5; // 윷의 시작한 파랑색 원의 번호
}
}
if (Next >= MoveNum[Start_Circle]) Finish = true; // 도착점에 도달했다면 체크.
return{ Prev, Next, Start_Circle, Select_Circle, Finish };
}
bool Check_Special_Point(int Circle, int Pos)
{
/* 특별한 점에 다른 윷이 존재하는지 판단하는 함수 */
if (Circle == 0)
{
/* 현재 이동하려는 윷이 파랑색 원에서 시작한 적이 없는 경우. */
/* '40'점이 설정되어있는 칸에 기존에 윷이 있는지 판단해 주면 된다.
/* '40'점이 있는 칸은, 빨강색, 파랑색, 초록색, 주황색 모두 겹치는 경로이기 때문 ! */
if (Pos == 20)
{
if (Visit[1][12] == true || Visit[2][16] == true || Visit[3][22] == true) return false;
}
else
{
if (Visit[0][Pos] == true) return false;
}
}
else if (Circle == 1)
{
/* 현재 윷이, 1번 파랑 원에서 시작해서 움직이고 있는 경우 */
/* 빨강색 / 파랑색 / 주황색 경로가 겹치는 "25", "30", "35", "40" 을 체크해 주어야 한다. */
if (Visit[2][Pos + 4] == true || Visit[3][Pos + 10] == true) return false;
if (Pos == 12)
{
if (Visit[0][20] == true) return false;
}
}
else if (Circle == 2)
{
if (Visit[1][Pos - 4] == true || Visit[3][Pos + 6] == true) return false;
if (Pos == 16)
{
if (Visit[0][20] == true) return false;
}
}
else if (Circle == 3)
{
if (Visit[1][Pos - 10] == true || Visit[2][Pos - 6] == true) return false;
if (Pos == 22)
{
if (Visit[0][20] == true) return false;
}
}
return true;
}
bool Check_Visit(STATE S, int Idx){
/* 현재 윷이 움직일 수 있는지를 판단해주는 함수 */
/* 판단해 줘야 할 것
1. 움직이려는 좌표가 특별한 점인지 ?
- 특별한 점이라면 다른 경로를 통해서 해당 좌표에 있는 놈들도 Check.
2. 움직이려는 좌표에 다른 윷이 존재하는지 ?
*/
if (Special_Point[S.Start_Circle][S.Next] == true)
{
if (Check_Special_Point(S.Start_Circle, S.Next) == false) return false;
}
if (Visit[S.Start_Circle][S.Next] == true) return false;
return true;
}
void MakeState(STATE S, int Idx, bool T)
{
/* 방문 체크를 하고, 실제로 윷을 옮기는 함수. */
/* T = true 일 경우, 실행 */
/* T = false 일 경우, 실행 취소 */
if (T == true)
{
if (S.Finish == true)
{
/* 현재 턴의 움직임으로 윷이 도착점에 도달했다면 ?? */
Visit[S.Start_Circle][S.Prev] = false; // 기존 좌표 방문 체크 해제
Yut[Idx].Pos = S.Next; // 현재 윷의 좌표 갱신
Yut[Idx].Finish = true; // 끝났음을 표시.
// 마지막 좌표는 윷이 있어도 다른 윷이 올 수 있기 때문에, 해당 좌표에 방문표시는 하지 않음 !
}
else
{
/* 현재 턴의 움직임으로 윷이 도착점에 도달하지는 않은 경우 */
if (S.Select_Circle == true)
{
/* 현재 턴의 움직임으로 파랑원의 번호가 결정 되었다면 ?*/
Yut[Idx].Blue_Circle = S.Start_Circle; // 윷의 파랑 원의 번호를 설정
Visit[0][S.Prev] = false; // 기존의 좌표 체크 해제.
/* 이번 턴에 파랑원의 번호가 결정되었다 = 기존에는 파랑원이 결정되지 않은 상태였다.
즉, 기존의 좌표 체크 해제는 파랑원이 결정되지 않은 Visit[0][S.Prev]로 해준다.
*/
}
else
{
/* 현재 턴의 움직임으로 파랑원의 번호가 결정되지 않았다. */
/* 이미 정해져있었을 수도, 아니면 아직 정해지지 않은 것일수도 있다. */
Visit[Yut[Idx].Blue_Circle][S.Prev] = false;
/* 기존의 좌표 체크 해제. 파랑원이 기존에 정해졌는지 아직 안정해졌는지는 모르지만
이번 턴에 결정되지는 않았으니까, Yut[Idx].BlueCircle 로 파랑원의 번호를 대체 */
}
Visit[Yut[Idx].Blue_Circle][S.Next] = true; // 방문 체크
Yut[Idx].Pos = S.Next; // 좌표갱신
Yut[Idx].Score = Yut[Idx].Score + MAP_Score[Yut[Idx].Blue_Circle][S.Next]; // 점수갱신
}
}
else
{
/* 실행 취소하는 과정 */
if (S.Finish == true)
{
/* 이번 턴으로 윷이 도착점에 도달했다면 ? */
Visit[S.Start_Circle][S.Prev] = true; // 기존의 좌표 방문 체크
Yut[Idx].Pos = S.Prev; // 좌표값 되돌리기
Yut[Idx].Finish = false; // 끝남표시 해제
}
else
{
if (S.Select_Circle == true)
{
/* 이번 턴으로 인해서 파랑색 원이 결정 되었는데, 이를 실행취소 한다. */
Visit[0][S.Prev] = true; // 기존에는 아직 파랑원이 정해져 있지 않았는데, 그 좌표로 돌아가야 하므로 Visit[0][S.Prev]
Visit[Yut[Idx].Blue_Circle][S.Next] = false;
Yut[Idx].Pos = S.Prev;
Yut[Idx].Score = Yut[Idx].Score - MAP_Score[Yut[Idx].Blue_Circle][S.Next];
Yut[Idx].Blue_Circle = 0; // 선택한 파랑원의 번호 다시 0으로 갱신.
}
else
{
Visit[Yut[Idx].Blue_Circle][S.Prev] = true;
Visit[Yut[Idx].Blue_Circle][S.Next] = false;
Yut[Idx].Pos = S.Prev;
Yut[Idx].Score = Yut[Idx].Score - MAP_Score[Yut[Idx].Blue_Circle][S.Next];
}
}
}
}
void DFS(int Cnt){
if (Cnt == 10){
int Temp = 0;
for (int i = 0; i < 4; i++){
Temp = Temp + Yut[i].Score;
}
if (Answer < Temp) {
Answer = Temp;
}
return;
}
for (int i = 0; i < 4; i++){
if (Yut[i].Finish == true) {
continue;
}
STATE State = GetState(i, Cnt);
if (Check_Visit(State, i) == false){
continue;
}
MakeState(State, i, true);
DFS(Cnt + 1);
MakeState(State, i, false);
}
int main(void){
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
//윷놀이판 만들기
/*
1. 각 경로 별 움직여야 하는 최대 칸수를 저장하는 배열 Move_Num에 값 삽입.
2. 특별한 점들 체크.
3. 점수 판 만들기
*/
MoveNum[0] = 21;
MoveNum[1] = 13;
MoveNum[2] = 17;
MoveNum[3] = 23;
Special_Point[1][9] = Special_Point[1][10] = Special_Point[1][11] = Special_Point[1][12] = true;
Special_Point[2][13] = Special_Point[2][14] = Special_Point[2][15] = Special_Point[2][16] = true;
Special_Point[3][19] = Special_Point[3][20] = Special_Point[3][21] = Special_Point[3][22] = true;
Special_Point[0][1] = Special_Point[0][2] = Special_Point[0][3] = Special_Point[0][4] = Special_Point[0][5] = Special_Point[0][20] = true;
for (int i = 1; i <= 20; i++) {
MAP_Score[0][i] = 2 * i;
}
MAP_Score[1][6] = 13;
MAP_Score[1][7] = 16;
MAP_Score[1][8] = 19;
MAP_Score[1][9] = 25;
MAP_Score[1][10] = 30;
MAP_Score[1][11] = 35;
MAP_Score[1][12] = 40;
MAP_Score[2][11] = 22;
MAP_Score[2][12] = 24;
MAP_Score[2][13] = 25;
MAP_Score[2][14] = 30;
MAP_Score[2][15] = 35;
MAP_Score[2][16] = 40;
MAP_Score[3][16] = 28;
MAP_Score[3][17] = 27;
MAP_Score[3][18] = 26;
MAP_Score[3][19] = 25;
MAP_Score[3][20] = 30;
MAP_Score[3][21] = 35;
MAP_Score[3][22] = 40;
for (int i = 1; i <= 5; i++) {
MAP_Score[1][i] = MAP_Score[0][i];
}
for (int i = 1; i <= 10; i++) {
MAP_Score[2][i] = MAP_Score[0][i];
}
for (int i = 1; i <= 15; i++) {
MAP_Score[3][i] = MAP_Score[0][i];
}
//input
for (int i = 0; i < 10; i++) cin >> Command[i];
DFS(0);
cout << Answer << endl;
return 0;
}'알고리즘문제 풀이 > 백준' 카테고리의 다른 글
| 백준 19636 요요 시뮬레이션 C++ (0) | 2023.10.09 |
|---|---|
| 백준 17837 새로운 게임 2 C++ (0) | 2023.10.09 |
| 백준 17779 게리맨더링 2 C++ (0) | 2023.10.02 |
| 백준 17496 스타후르츠 C++ (1) | 2023.10.02 |
| 백준 17256 달달함이 넘쳐흘러 C++ (1) | 2023.10.02 |