[풀이]백준 풀이(XI/16928 등 )

문제풀이

Maaaaaaaze(16985, BFS, 시뮬레이션, 경우의 수)

  • 5x5 2차원 배열 5개를 쌓는다 (5x5x5 3차원 배열)
  • 회전 가능
  • 3차원 배열이므로 tuple 사용
  • 쌓는 순서 변경가능 -> next_permutation 사용

따라서

  1. 입력을 받는다
  2. 큐브를 생성한다
    • 회전
    • 쌓는 순서 변경
  3. bfs를 실행한다.

순으로 진행했다.

회전

rotation함수를 생성해서 회전을 구현했으나 괴랄한 for문이 생성되었다.

확인한 결과, rotation을 계속 실행하는 건 매우 비효율적이므로 dp처럼 rotation을 한 번씩 실행해서 모두 저장해놓는 것이 빠르게 실행하는데 도움이 된다.

아래 코드 틀린답입니다.

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <tuple>
#define INF 987654321
using namespace std;
vector<int> floatIndex;
int tempmaze[5][5][5];
int realMaze[5][5][5];
bool check[5][5][5];
int dz[6] = { 1, 0, 0, 0, 0, -1, };
int dy[6] = { 0, 1, 0, -1, 0, 0 };
int dx[6] = { 0, 0, 1, 0, -1, 0 };
int Answer=INF;

void init() {
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
				for (int k = 0; k < 5; k++) {
						tempmaze[i][j][k] = 0;
						realMaze[i][j][k] = 0;
						check[i][j][k] = false;
				}
		}
	}
}

bool isInside(int x, int y, int z) {
	if (x >= 0 && y >= 0 && z >= 0 && x < 5 && y < 5 && z < 5) return true;
	else return false;
}


void saveMaze() {
	for (int x = 0; x < 5; x++) {
		for (int y = 0; y < 5; y++) {
			for (int z = 0; z < 5; z++) {
				tempmaze[x][y][z] = realMaze[x][y][z];
			}
		}
	}
}

void loadMaze() {
	for (int x = 0; x < 5; x++) {
		for (int y = 0; y < 5; y++) {
			for (int z = 0; z < 5; z++) {
				realMaze[x][y][z] = tempmaze[x][y][z];
			}
		}	
	}
}

//rotation = right, 3rotation = left; 
void rotation(int x) {
	//(x,y) = (y, 4-x)
	for (int y = 0; y < 5; y++) {
		for (int z = 0; z < 5; z++) {
			//회전... .이렇게 해도 되나?
			int temp = realMaze[x][z][4 - y];
			realMaze[x][y][z] = temp;
		}
	}
}


void bfs(int sx, int sy, int sz) {
	//회전 1. (중복발생)
	for (int idx1 = 0; idx1 < 5; idx1++) {
		for (int idx2 = 0; idx2 < 5; idx1++) {
			for (int idx3 = 0; idx3 < 5; idx1++) {
				for (int idx4 = 0; idx4 < 5; idx1++) {
					for (int idx5 = 0; idx5 < 5; idx1++) {
						//cout << "idx5=" << idx5 << "\n";


						saveMaze();
						for (int a = 0; a < idx1; a++) {
							rotation(0);
						}
						for (int a = 0; a < idx2; a++) {
							rotation(1);
						}
						for (int a = 0; a < idx3; a++) {
							rotation(2);
						}
						for (int a = 0; a < idx4; a++) {
							rotation(3);
						}
						for (int a = 0; a < idx5; a++) {
							rotation(4);
						}


						queue<tuple<int, int, int>> q;
						q.push(tuple<int, int, int>(sx, sy, sz));
						//move, bfs


						int Answer_candidate = 0;
						while (!q.empty()) {
							tuple<int, int, int> t = q.front();
							q.pop();
							for (int i = 0; i < 6; i++) {
								
								
								int nx = get<0>(t) + dx[i];
								int ny = get<1>(t) + dy[i];
								int nz = get<2>(t) + dz[i];
								
								
								if (realMaze[nx][ny][nz] == 1 && isInside(nx, ny, nz) && !check[nx][ny][nz])
								{
									check[nx][ny][nz] = true;
									q.push(tuple<int, int, int>(nx, ny, nz));
									Answer_candidate++;
								}
							}
						}
						Answer = min(Answer, Answer_candidate);
						//realMaze 원복
						loadMaze();
					}
				}
			}
		}
	}
	//이동
//판 위 아래 변경
}
void makeCube() {
	int x = 0, y = 0, z = 0;
	do {
		for (x = 0; x < 5; x++) {
			//cout << "realMaze Index = x, y, z" << x << "," << y << "," << z;
			//cout << "  tempMaze Index = x, y, z" << floatIndex[x] << "," << y << "," << z;
			for (y = 0; y < 5; y++) {
				for (z = 0; z < 5; z++) {
					realMaze[x][y][z] = tempmaze[floatIndex[x]][y][z];
				}
			}
			//cout << endl;
		}
		bfs(0, 0, 0);
	} while (next_permutation(floatIndex.begin(), floatIndex.end()));
}
int main() {
//초기화
	std::ios::sync_with_stdio(false);
	init();
//입력
	for (int i = 0; i < 5; i++) {
		floatIndex.push_back(i);
		for (int j = 0; j < 5; j++) {
			for (int k = 0; k < 5; k++) {
				cin >> tempmaze[i][j][k];
			}
		}
	}
	makeCube();
	if (!check[4][4][4]) Answer = -1;
	cout << Answer;
}

풀이 (정답)

  • 일단 큐브를 만들고 나서야 BFS로 탈출하는데 걸리는 시간을 구할 수 있다.
  • 판을 쌓는 방법 5!
  • 큐브를 회전시키는 방법 4^5
  • 약 122,880개의 큐브에서 (0,0,0) to (4,4,4) bfs
  • BFS는 최대 125개의 칸을 거치고 6개의 방향을 고려하면
  • 약 92,160,000개으 경우의 수가 나온다.

입력부분과 bfs부분에서 부족한게 있었다.. 나머지는 거의 동일했으니 이제 고지에 올라가자 ㅠㅠ,,,,

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <tuple>
#define INF 987654321
using namespace std;
//board[dir][i][j][k]: i번째 판을 시계방향으로 dir번 돌렸을 때 (j,k)의 값
int board[4][5][5][5];
int maze[5][5][5];
int dist[5][5][5];
int dx[6] = {1,0,0,0,0,-1};
int dy[6] = {0,1,-1,0,0,0};
int dz[6] = {0,0,0,1,-1,0};
int Answer = INF;

void init(){
	for(int i=0; i<5; i++){
		for(int j=0; j<5; j++){
			for(int k=0; k<5; k++){
				dist[i][j][k] = -1;
			}
		}
	}
}
bool isInside(int x, int y, int z) {
	if (x >= 0 && y >= 0 && z >= 0 && x < 5 && y < 5 && z < 5) return true;
	else return false;
}

int solve(int sx, int sy, int sz){
	if(maze[0][0][0] == 0 || maze[4][4][4] == 0) return INF;
	init();
	queue<tuple<int,int,int>> q;
	q.push(tuple<int, int, int>(sx,sy,sz));
	dist[sx][sy][sz] = 0;

	while(!q.empty()){
		tuple<int, int, int> t = q.front();
		q.pop();

		for(int dir=0; dir<6; dir++){
			int nx,ny,nz;
			tie(nx,ny,nz) = t;
			nx += dx[dir];
			ny += dy[dir];
			nz += dz[dir];
			if(!isInside(nx,ny,nz)) continue;
			//다음 미로가 막혀있거나, 방문한 적이 있으면 PASS
			if(maze[nx][ny][nz] == 0 || dist[nx][ny][nz] != -1) continue;
			//마지막칸에 도달했으면 끝
			int xtmp, ytmp, ztmp;
			tie(xtmp,ytmp,ztmp) = t;
			if(nx == 4 && ny == 4 && nz==4) return dist[xtmp][ytmp][ztmp];

			dist[nx][ny][nz] = dist[xtmp][ytmp][ztmp]+1;
			q.push(tuple<int, int, int>(nx,ny,nz));
		}
	}
	return INF;
}

int main(){
	//flush and speed
	ios::sync_with_stdio(0);
	cin.tie(0);
	//회전같긴한데,,,
	for(int i = 0; i < 5; i++){
		for(int j = 0; j < 5; j++)
		  for(int k = 0; k < 5; k++)
			//원래형태 입력
			scanf("%d",&board[0][i][j][k]);

	for(int j = 0; j < 5; j++)
		  for(int k = 0; k < 5; k++)
			//1회전 입력
			board[1][i][j][k] = board[0][i][4-k][j];

	for(int j = 0; j < 5; j++)
		  for(int k = 0; k < 5; k++)
			//1회전 + 1회전 입력
			board[2][i][j][k] = board[1][i][4-k][j];


	for(int j = 0; j < 5; j++)
		  for(int k = 0; k < 5; k++)
			//2회전 +1회전 입력
			board[3][i][j][k] = board[2][i][4-k][j];
	}

	int order[5] = {0,1,2,3,4}; // 판을 쌓는 순서
	do{
		//4^5
		for(int i=0; i<1024; i++){
			int tmp = i; //5개의 판의 dir을 정해주는 변수
			for(int j=0; j<5; j++){
				int dir = tmp%4;
				tmp /=4;
				for(int k=0; k<5; k++){
					for(int l=0; l<5; l++){
						maze[j][k][l] = board[dir][order[j]][k][l];
					}
				}
			}
			Answer = min(Answer, solve(0,0,0));
		}
	}while(next_permutation(order, order+5));


	if(Answer == INF) cout << -1;
	else cout << Answer+1;

}

2146(다리만들기, bfs)

  • 2차원 배열이 있다.
  • 모든 섬을 찾아낸 뒤, 모든 육지에서 bfs를 실행하여 min값을 출력하면 된다.

58프로에서.. 계속틀린다… 다시짜야하나……

#include <iostream>
#include <queue>
#include <algorithm>
#include <math.h>

#define INF 987654321
using namespace std;

int a, groupIdx=INF, z=0;
int map[100][100];
int dist[100][100];
bool check[100][100];
///////////////////////////////
int Answer = INF;
int group[100];
bool check2[100][100];
int dist2[100][100];


int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};

void init(){
	for(int i=0; i<a; i++){
		for(int j=0; j<a; j++){
			map[i][j]=0;
			dist[i][j]=0;
			check[i][j]=false;
			check2[i][j]=false;
		}
	}
}

bool isInside(int x, int y){
	if(x>=0 && y>=0 && x<a && y<a) return true;
	else return false;
}


//문제 dist값이 갱신이 안돼
void bfs(int sx, int sy){
	queue<pair<int,int>> q;
	q.push(make_pair(sx,sy));
	//방문체크
	dist[sx][sy]=groupIdx;
	check[sx][sy]=true;

	while(!q.empty()){
		pair<int,int> n;
		n = q.front();
		q.pop();
		int nx, ny;
		for(int i=0; i<4; i++){
			nx = n.first + dx[i];
			ny = n.second+ dy[i];
			//온적이 없고 범위안이고 갈 수 있으면
			if(dist[nx][ny]== 0 && isInside(nx,ny) && !check[nx][ny] && map[nx][ny]==1)
			{
				//그룹화 && 큐에 삽입
				dist[nx][ny] = groupIdx;
				check[nx][ny] = true;
				q.push(make_pair(nx,ny));
			}
		}
	}
	group[z] = groupIdx;
	groupIdx--;
	z++;
}

int bfs2(int count){
	queue<pair<int,int>> q;
	int startValue;

	//우선 섬의 좌표를 다 넣는다.
	for(int i=0; i<a; i++){
		for(int j=0; j<a; j++){
			if(dist[i][j]== INF-count)
			{
				check2[i][j] = true;
				q.push(make_pair(i,j));
				startValue = dist[i][j];
			}
		}
	}

	int result =0;

	while(!q.empty()){
		int size = q.size();
		for(int i=0; i<size; i++){
			//현재위치
			int cx = q.front().first;
			int cy = q.front().second;
			q.pop();

			for(int i=0; i<4; i++){
				//다음위치
				int nx = cx + dx[i];
				int ny = cy + dy[i];

				if(isInside(nx,ny))
				{
					//다른 그룹에 도달한 경우 반환
					if(dist[nx][ny]>0 && dist[nx][ny]!= startValue)
					{
						return result;
					}
					//방문하지 않은 경우
					else if (dist[nx][ny]==0 && !check2[nx][ny])
					{
						check2[nx][ny] = true;
						q.push(make_pair(nx,ny));
					}
				}
			}
		}
		result++;
	}
	return 9999;
}


int main(){

	init();

	cin >> a;
	for(int i=0; i<a; i++){
		for(int j=0; j<a; j++){
			cin >> map[i][j];
		}
	}
	//그룹화 완료
	for(int i=0; i<a; i++){
		for(int j=0; j<a; j++){
			if(map[i][j]==1 && dist[i][j]<=0)
			{
				bfs(i,j);

			}
		}
	}
	//새로 bfs

	for(int i=0; i<z; i++){

		//초기화
		for(int j=0; j<a; j++){
			for(int k=0; k<a; k++){
				check2[j][k] = false;
			}
		}

		//i = 그룹
		Answer = min(Answer, bfs2(i));
	}
	if(Answer == 9999) cout << 0;
	else cout << Answer << endl;

	return 0;
}

tip: Flood fill

TIP

  • vector, tuple, pair을 함수의 인자로 넘기면 복사본을 넘기므로 원본의 값이 변하지 않는다. 아래 함수를 실행하고 cout을 찍어도 1,1이 나옴. ex)
    void func1(pair<int, int>p){
      p.first++;
    }
    int main(){
      pair<int,int> p ={1,1};
      func1(p)
    }
    
  • tie함수를 쓰면 쉽게 값을 뽑을 수 있다.
    int x1,y1,z1, x2,y2,z2;
    tuple<int,int,int> a = {2,3,4}
    x1 = get<0>(t), y1 = get<1>(t), z1=get<2>(t);
    tie(x2,y2,z2);
    
  • 모든 경우의 수를 따져야 할때는 next_permutation()을 활용하자

tip1

인싸들의 가위바위보(16986)

회고

  • 디버깅한다고 중간중간 시간을 많이 까먹었다 ㅎㅎ
  • 마지막 코드랑 차이를 비교할 것!

아래사진부분의 코드가 다른데… 테스트가 필요해보인다.

inssa

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

int n, k;
int jwCount, khCount, mhCount;
//상성을 나타내는 표
int a[9][9];
bool win;
vector<int> jwTemp;
//경희, 민호의 순서
queue<int> jw;
queue<int> kh;
queue<int> mh;

void init() {
	for (int i = 0; i < 9; i++) {
		for(int j = 0; j < 9; j++) {
			a[i][j] = -1;
		}
	}
}

//들어올 떄 우선순위를 정해서 들어갈 것
void fight(int winner, int next) {

	
	//priority = true이면 무승부 시 winner가 이김

	int x, y;

	if (win) {
		return;
	}

	else 
	{
		//지우가 승수를 만족했을 때
		if (jwCount == k)
		{
			win = true;
			return;
		}
		if (jw.empty()) {
			win = false;
			return;
		}
		//다른 사람이 우승했을 때
		if (khCount == k || mhCount == k) {
			win = false;
			return;
		}

		//이전 경기 승자 && 안싸운 사람 필요. (1 = jw, 2 = kh, 3 = mh)
		if (winner == 1 && next == 2)
		{
			x = jw.front(); jw.pop();
			y = kh.front(); kh.pop();

			//x가 y를 이김 (jw승)
			if (a[x][y] == 2)
			{
				jwCount++;
				fight(1, 3);
			}
			else {
				khCount++;
				fight(2, 3);
			}
		}
		else if (winner == 1 && next == 3)
		{
			x = jw.front(); jw.pop();
			y = mh.front(); mh.pop();

			//x가 y를 이김 (jw승)
			if (a[x][y] == 2)
			{
				jwCount++;
				fight(1, 2);
			}
			//비기든 지든 mh승
			else {
				mhCount++;
				fight(3, 2);
			}
		}
		else if (winner == 2 && next == 1)
		{
			x = kh.front(); kh.pop();
			y = jw.front(); jw.pop();

			//x가 y를 이김 (jw승)
			if (a[x][y] == 0)
			{
				jwCount++;
				fight(1, 3);
			}
			//비기든 이기든 kh승
			else {
				khCount++;
				fight(2, 3);
			}
		}
		else if (winner == 2 && next == 3)
		{
			x = kh.front(); kh.pop();
			y = mh.front(); mh.pop();

			//x가 y를 이김 (kh승)
			if (a[x][y] == 2)
			{
				khCount++;
				fight(2, 1);
			}
			//비기든 지든 mh승
			else {
				mhCount++;
				fight(3, 1);
			}
		}
		else if (winner == 3 && next == 1)
		{
			x = mh.front(); mh.pop();
			y = jw.front(); jw.pop();

			//y가 x 이김 (jw승)
			if (a[x][y] == 0)
			{
				jwCount++;
				fight(1, 2);
			}
			//비기든 이기든 mh승
			else {
				mhCount++;
				fight(3, 2);
			}
		}
		else if (winner == 3 && next == 2)
		{
			x = mh.front(); mh.pop();
			y = kh.front(); kh.pop();

			//y가 x 이김 (kh승)
			if (a[x][y] == 0)
			{
				khCount++;
				fight(2, 1);
			}
			//비기든 이기든 mh승
			else {
				mhCount++;
				fight(3, 1);
			}
		}

	}

}

int main() {
	std::ios::sync_with_stdio(false);
	cin.tie(0);

	cin >> n >> k;
	//정상 동작 확인 완료
	init();

	//상성입력
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> a[i][j];
		}
	}

	//경희 20개
	queue<int> khTemp;
	queue<int> mhTemp;
	for (int a = 0; a < 20; a++) {
		int temp;
		cin >> temp;
		kh.push(temp-1);
	}
	//민호 20개
	for (int a = 0; a < 20; a++) {
		int temp;
		cin >> temp;
		mh.push(temp-1);
	}
	khTemp = kh;
	mhTemp = mh;
	//지우 1~N 손동작
	for (int a = 0; a < n; a++) {
		jwTemp.push_back(a);
	}

	win = false;

	//지우가 할 수 있는 손동작 조합을 1줄씩 던진다.
	do {
		//정상입력 확인
		for (int i = 0; i < n; i++) {
			jw.push(jwTemp[i]);
			//지우 순서 확인
			//cout << "jw[" << i << "] = " << jwTemp[i] << " ";
		}
		mh = mhTemp;
		kh = khTemp;
		//cout << endl;
		jwCount = 0;
		khCount = 0; 
		mhCount = 0;
		//지우, 경희로 시작 -> fight 안에서 끝내고와라.
		fight(1, 2);
		//지우 초기화
		while (!jw.empty()) {
			jw.pop();
		}
	} while (next_permutation(jwTemp.begin(), jwTemp.end()));

	//3k-2 경기 후 누군가는 이긴다.
	//지우가 모든 손동작을 다냈을 때 못이겼으면 0출력
	//무승부 시 뒷사람이 이긴다.
	//이긴사람 & 참여하지 않았던 사람이 다음경기를 진행한다.
	cout << win;
}

댓글남기기