문제 링크


www.acmicpc.net/problem/20056

 

20056번: 마법사 상어와 파이어볼

첫째 줄에 N, M, K가 주어진다. 둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다. 서로 다른 두 파이어볼의 위치

www.acmicpc.net

 

접근 방법


문제에 제시된 대로 구현을 하면 됩니다.

큰 범주로 나누어 본다면

 

1. 파이어볼이 자신의 정보를 가지고 이동

2. 격자 정보에 따라 파이어볼을 합치고 분해하여 파이어볼의 정보 갱신

 

1. 파이어볼의 이동은 2가지 동작으로 구분 지었습니다.

 

1-1. 파이어볼을 이동시키기 전 격자를 스캔하며 파이어볼의 정보를 vector<fire> fires에 저장함과 동시에 격자에서 제거를 합니다.

 제거를 하는 이유는 fires의 정보를 가지고 파이어볼들을 재배치를 하기 위함입니다. 또한 미리 fires에 정보를 저장하는 이유는 격자에서 바로바로 옮기게 되면 현재 보고 있는 불이 이동을 했는지 아닌지에 대한 검사를 하는 과정이 더 요구 될 것이기 때문입니다.

void moveAll() {

	vector<fire> fires;

	for (int i = 0; i < N; i++)  //격자에서 불을 제거하며 정보를 벡터에 담는다.
		for (int j = 0; j < N; j++) 
			while(!map[i][j].empty()){
				fire elem = map[i][j].back();
				map[i][j].pop_back();
				fires.push_back(elem);
			}

	for (int i = 0; i < fires.size(); i++)
		moveOne(fires[i]);
}

 

1-2. 이제 1-1에서 fires에 저장한 정보를 바탕으로 파이어볼을 하나씩 이동시킬 것입니다. 

 이동 시킬 때의 조건으로는 격자에서 파이어볼이 순회하는 구조이므로 좌표가 N - 1을 벗어난다면 0으로, 0을 벗어난다면  N - 1로 이동시켜 주어야 합니다. 

 또한 파이어볼을 방향으로 속도의 횟수 만큼 한 칸씩 이동시켜도 시간초과에는 걸리지 않으나 한 파이어볼에 대해 최대 1000번의 반복문이 연산이 될 수 있으므로 주기를 검사해 반복되는 연산을 최소화 시켜주어야 합니다.

 

만약 4X4격자에서 S지점에서 오른쪽으로 11칸을 진행한다고 하면 아래 그림과 같습니다.

여기서 주기는 4가 될 것이며 주기만큼 이동하면 자신의 원래 위치로 오게 될 것 입니다. 즉 4, 8일 때 출발위치와 같아질 것입니다. 그렇다면 남은거리는 leftDistance = (totalDistance) % N 이 될 것이며 남은 거리만큼 범위를 신경써서 이동시키면 됩니다.

void moveOne(fire elem) {

	int next_x = elem.x + direction[elem.d].first * elem.s;
	int next_y = elem.y + direction[elem.d].second * elem.s;

	if (!inRange(next_x, next_y)) {
		
		next_x = elem.x;
		next_y = elem.y;
		int leftDist = elem.s % N;

		while (--leftDist >= 0) {
			next_x += direction[elem.d].first;
			next_y += direction[elem.d].second;
			next_x = (next_x > N - 1) ? 0 : next_x;
			next_x = (next_x < 0) ? N - 1 : next_x;
			next_y = (next_y > N - 1) ? 0 : next_y;
			next_y = (next_y < 0) ? N - 1 : next_y;
		}
	}
	map[next_x][next_y].push_back({ next_x, next_y, elem.m, elem.s, elem.d });
}

 

 

2. 파이어볼의 이동 이후 격자를 스캔하며 2개 이상의 파이어볼이 있는 곳에 대해 합과 분해를 합니다.

void processing() { //합과 분해

	for (int i = 0; i < N; i++) 
		for (int j = 0; j < N; j++) 
			if (map[i][j].size() > 1) {
				fire fireball = composition(map[i][j]); //합쳐진 파이어볼의 정보
				decomposition(fireball);
			}
}

 

2-1. 합의 과정에 있어서 방향을 설정하는 것은 아래와 같은 코드로 구현을 하였습니다.

if (tmp == ball.d % 2)
	dir *= 1;
else
	dir *= 0;

 tmp는 이전 방향의 정보이며 현재 방향과 일치하는지 비교를 합니다. dir은 1로 초기화 되어있으며 한 번이라도 방향이 이전과 달라진다면 dir은 0이 될 것이며 이 후에는 어떤 수를 dir에 곱해주어도 dir은 0을 유지합니다.

 

소스 코드


#include <iostream>
#include <vector>

using namespace std;

typedef struct fire {
	int x, y, m, s, d;
};

pair <int, int> direction[8] = { {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1} };
vector<fire> map[52][52];
int N, M, K;

bool inRange(int x, int y) {
	return (0 <= x && x < N && 0 <= y && y < N);
}

int messSum() {

	int sum = 0;

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			vector<fire> area = map[i][j];
			for (fire &one : area) 
				sum += one.m;
		}
	}
	return sum;
}

void decomposition(fire &fireball) { //분해

	//방향 처리를 위한 숫자 전환
	//모든 방향이 홀이거나 짝수이면 dir은 0 아니라면 1
	int d = (fireball.d == 0) ? 1 : 0; 

	for (int i = 0; i < 4; i++) 
		if(fireball.m > 0)
			map[fireball.x][fireball.y].push_back({ fireball.x, fireball.y, fireball.m, fireball.s, d + (i * 2) });
}

fire composition(vector<fire> &area) { //합

	int dir = 1;
	int cnt = area.size();
	int mSum = 0 , sSum = 0;
	int tmp = area.back().d % 2;
	int x = area.back().x;
	int y = area.back().y;
	
	while (!area.empty()) {
		
		fire ball = area.back();
		area.pop_back();

		mSum += ball.m;
		sSum += ball.s;

		if (tmp == ball.d % 2)
			dir *= 1;
		else
			dir *= 0;
		tmp = ball.d % 2;
	}
	//모든 방향이 홀이거나 짝수이면 dir은 1 반환, 아니라면 0반환
	return { x, y, mSum / 5, sSum / cnt, dir };
}

void processing() { //합과 분해

	for (int i = 0; i < N; i++) 
		for (int j = 0; j < N; j++) 
			if (map[i][j].size() > 1) {
				fire fireball = composition(map[i][j]); //합쳐진 파이어볼의 정보
				decomposition(fireball);
			}
}

void moveOne(fire elem) {

	int next_x = elem.x + direction[elem.d].first * elem.s;
	int next_y = elem.y + direction[elem.d].second * elem.s;

	if (!inRange(next_x, next_y)) {
		
		next_x = elem.x;
		next_y = elem.y;
		int leftDist = elem.s % N;

		while (--leftDist >= 0) {
			next_x += direction[elem.d].first;
			next_y += direction[elem.d].second;
			next_x = (next_x > N - 1) ? 0 : next_x;
			next_x = (next_x < 0) ? N - 1 : next_x;
			next_y = (next_y > N - 1) ? 0 : next_y;
			next_y = (next_y < 0) ? N - 1 : next_y;
		}
	}
	map[next_x][next_y].push_back({ next_x, next_y, elem.m, elem.s, elem.d });
}

void moveAll() {

	vector<fire> fires;

	for (int i = 0; i < N; i++)  //맵에서 불을 제거하며 정보를 벡터에 담는다.
		for (int j = 0; j < N; j++) 
			while(!map[i][j].empty()){
				fire elem = map[i][j].back();
				map[i][j].pop_back();
				fires.push_back(elem);
			}

	for (int i = 0; i < fires.size(); i++)
		moveOne(fires[i]);
}

int solve() {

	while (--K >= 0) {
		moveAll();
		processing();
	}
	return messSum();
}

int main() {

	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin >> N >> M >> K;

	for (int i = 0; i < M; i++) {
		int r, c, m, s, d;
		cin >> r >> c >> m >> s >> d;
		map[r - 1][c - 1].push_back({ r - 1,c - 1,m,s,d });
	}
	cout << solve();
}

문제 링크


www.acmicpc.net/problem/20057

 

20057번: 마법사 상어와 토네이도

마법사 상어가 토네이도를 배웠고, 오늘은 토네이도를 크기가 N×N인 격자로 나누어진 모래밭에서 연습하려고 한다. 위치 (r, c)는 격자의 r행 c열을 의미하고, A[r][c]는 (r, c)에 있는 모래의 양을

www.acmicpc.net

 

접근 방법


 시뮬레이션이나 구현 류의 문제들은 초기 설계를 어떻게 하느냐에 따라 코드 길이가 차이가 많이 날 수 있으니 초반 설계를 잘하는 것이 중요하다. 코드가 길어진다는 것은 구현이 복잡해질 가능성이 높다는 의미이다.

 

1. 상, 하, 좌, 우 방향에 따른 상대적인 좌표와 그 비율을 미리 저장한다.

typedef struct info{
	int x, y, cost;
};

vector<vector<info>> infoV;

void init() {
	// left, right는 요소의 2번째 인자의 부호를 바꿔주면 되고, up, down은 요소의 1번째 인자의 부호를 전환
	// 알파의 비율 부분은 모든 요소 처리 이후 따로 계산 
	infoV.push_back({ {-1,0,1}, {-1,-1,7}, {-2,-1,2}, {-1,-2,10}, {1,0,1}, {1,-1,7}, {2,-1,2}, {1,-2,10}, {0,-3,5} }); //left
	infoV.push_back({ {0,-1,1}, {1,-1,7}, {1,-2,2}, {2,-1,10}, {0,1,1}, {1,1,7}, {1,2,2}, {2,1,10}, {3,0,5} });  //down
	infoV.push_back({ {-1,0,1}, {-1,1,7}, {-2,1,2}, {-1,2,10}, {1,0,1}, {1,1,7}, {2,1,2}, {1,2,10}, {0,3,5} });   //right
	infoV.push_back({ {0,-1,1}, {-1,-1,7}, {-1,-2,2}, {-2,-1,10}, {0,1,1}, {-1,1,7}, {-1,2,2}, {-2,1,10}, {-3,0,5} }); // up
}

 

2. 문제 풀이가 종료 될 때까지 방향의 전환 횟수는 2*N - 1번 일어난다.

 현재 방향의 전환 횟수를 iter라고 하고, 좌,하,우,상의 방향을 순서대로 0,1,2,3이라고 하였을 때

(방향의 순서는 문제에서 제시된 대로 정했다.)

 (1) 현재진행방향 = (iter)%4

 (2) 진행방향크기 = (iter)%2 + 1(단, 마지막 진행방향에선 (iter)%2)

이러한 규칙을 찾아 낼 수 있다.

 

3. 위의 규칙에 따라 iter를 1씩 증가시키며 한 방향으로 진행하는 처리를 할 수 있다.

 

4.  3의 과정에서 한 칸씩 옮겨가는 과정을 처리하여 값을 얻어낸다.

 

소스 코드


#include <iostream>
#include <vector>

using namespace std;

typedef struct info{
	int x, y, cost;
};

vector<vector<info>> infoV;
pair<int, int> direction[4] = { {0,-1}, {1,0}, {0,1}, {-1,0} };
int map[1000][1000];
int N;

void init() {
	// left, right는 요소의 2번째 인자의 부호를 바꿔주면 되고, up, down은 요소의 1번째 인자의 부호를 전환
	// 알파의 비율 부분은 모든 요소 처리 이후 따로 계산 
	infoV.push_back({ {-1,0,1}, {-1,-1,7}, {-2,-1,2}, {-1,-2,10}, {1,0,1}, {1,-1,7}, {2,-1,2}, {1,-2,10}, {0,-3,5} }); //left
	infoV.push_back({ {0,-1,1}, {1,-1,7}, {1,-2,2}, {2,-1,10}, {0,1,1}, {1,1,7}, {1,2,2}, {2,1,10}, {3,0,5} });  //down
	infoV.push_back({ {-1,0,1}, {-1,1,7}, {-2,1,2}, {-1,2,10}, {1,0,1}, {1,1,7}, {2,1,2}, {1,2,10}, {0,3,5} });   //right
	infoV.push_back({ {0,-1,1}, {-1,-1,7}, {-1,-2,2}, {-2,-1,10}, {0,1,1}, {-1,1,7}, {-1,2,2}, {-2,1,10}, {-3,0,5} }); // up
}

bool inRange(int x, int y) {
	return (0 <= x && x < N && 0 <= y && y < N);
}

int onePoint(int x, int y, int dir) { 
// 한 지점에 대한 처리
	vector<info> spread = infoV[dir];
	int spreadSand = 0; // 지금까지 퍼져나간 모래, 알파지점을 계산하기 위한 값
	int outSand = 0;    // 범위 밖으로 나간 모래 -> 우리가 원하는 값
	int target_x = x + direction[dir].first;  //문제의 y지점의 행
	int target_y = y + direction[dir].second; //문제의 y지점의 열

	for (int i = 0; i < spread.size(); i++) {
		
		int cost = spread[i].cost * 0.01 * map[target_x][target_y];
		int next_x = x + spread[i].x;
		int next_y = y + spread[i].y;
		spreadSand += cost; //알파 지점의 값을 알기위해 퍼져나간 모래를 더함

		if (inRange(next_x, next_y)) //범위 내에 있다면 모래를 더해줌
			map[next_x][next_y] += cost; 
		else    // 범위 내에 없다면 밖으로 나간 모래 값을 더해줌
			outSand += cost;
	}
	
    // 알파 지점 처리
    //알파 지점은 현재 위치에서 진행방향으로 두 칸 떨어짐
	int next_x = x + direction[dir].first * 2; 
	int next_y = y + direction[dir].second * 2; 
    // 지금까지 퍼져나간 모래의 값을 y지점에서 빼준다.
	int cost = map[target_x][target_y] - spreadSand;
	map[target_x][target_y] = 0;

	if (inRange(next_x, next_y))
		map[next_x][next_y] += cost;
	else
		outSand += cost;
	
	return outSand;
}

info oneDirection(int dir, int len, int x, int y) {
//한 진행방향에 대한 처리
	int sum = 0;

	for (int i = 0; i < len; i++) {
		sum += onePoint(x, y, dir);
		x += direction[dir].first;
		y += direction[dir].second;
	}
	return { x,y,sum };
}

int solve() {

	int ans = 0;
	int iter = -1;
	int x = N / 2;
	int y = N / 2;

	while (++iter < N * 2 - 1) { //방향 전환 횟수
		int len = (iter == (N - 1) * 2) ? iter / 2 : iter / 2 + 1; //마지막 전환에선 len의 크기 증가 x
		info result = oneDirection(iter % 4, len, x, y);
		x = result.x;
		y = result.y;
		ans += result.cost;
	}

	return ans;
}

int main() {

	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	init();

	cin >> N;

	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++)
			cin >> map[i][j];

	cout << solve();
}

개발 환경 : VS2017

질문, 지적 환영합니다!

문제 링크


www.acmicpc.net/problem/19236

 

19236번: 청소년 상어

첫째 줄부터 4개의 줄에 각 칸의 들어있는 물고기의 정보가 1번 행부터 순서대로 주어진다. 물고기의 정보는 두 정수 ai, bi로 이루어져 있고, ai는 물고기의 번호, bi는 방향을 의미한다. 방향 bi는

www.acmicpc.net

 

접근 방법


바보같은 실수 하나로 오랜 시간을 잡아먹은 문제다. 

 필자가 했던 실수는 물고기를 이동시키지 않은 상태에서 상어가 먹을 수 있는 물고기가 있는지 확인하고 물고기를 이동시켰다는 점이다. 이렇게 구현을 한 상태에서 문제에서 주어진 테스트 케이스가 모두 맞아 떨어져 오류를 수정하기 힘들었다.

 

문제 풀이는 단순하다.

1. 상어를 0,0에 배치시키고 상태를 업데이트 한다.

2. DFS를 실행하는데 현재 4X4의 물고기들 상태를 저장해 두고 물고기를 이동시킨다.

3. 상어가 먹을 수 있는 먹이가 있는지 확인한다. 

4. 먹을 수 있다면 DFS를 진행 시키고 아니면 최대 값을 갱신하고 저장 해 둔 상태로 다시 복원한다.

 

물고기를 이동시키는 함수를 풀이하자면

1. 맵을 스캔하며 n번 물고기의 좌표를 vector<pair<int, int>> fishInfo에 저장한다.

void moveAll(info &shark) {

	vector<pair<int, int>> fishInfo(17, {-1,-1}); //n번 물고기와 좌표를 매핑

	for (int i = 0; i < 4; i++) 
		for (int j = 0; j < 4; j++) 
			if (!(i == shark.x && j == shark.y))
				if (map[i][j].num != 0) 
					fishInfo[map[i][j].num] = { i,j }; 
	
	for (int i = 1; i < 17; i++) 
		if (fishInfo[i].first != -1) 
			move(fishInfo, shark, i);
}

 

2. fishInfo를 1번 부터 탐색하며 한 마리 씩 이동시킨다. 이 때 물고기가 이동하며 swap되는 정보가 fishInfo에 실시간으로 갱신되야 하므로 move()함수에 fishInfo를 참조 연산자로 넘겨주었다.

void move(vector<pair<int, int>> &fishInfo, info shark, int num) {

	int x = fishInfo[num].first;
	int y = fishInfo[num].second;
	int dir = map[x][y].dir;

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

		int next_dir = (dir + i) % 8;
		int next_x = x + direction[next_dir].first;
		int next_y = y + direction[next_dir].second;
		
		if (inRange(next_x, next_y) && !(next_x == shark.x && next_y == shark.y)) { //이동가능 하다면 swap
			
			fish tmp = map[next_x][next_y];
			map[next_x][next_y] = { num, next_dir }; 
			map[x][y] = tmp;
			fishInfo[num] = { next_x, next_y };
			fishInfo[tmp.num] = { x,y };

			break;
		}
	}
}

 

자세한 내용은 하단의 소스코드에 주석을 남겨두었음

 

소스 코드


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

using namespace std;

typedef struct fish {
	int num, dir;
};

typedef struct info {
	int x, y, dir, cost;
};

pair<int, int> direction[8] = { {-1, 0}, {-1,-1}, {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1} };
fish map[4][4];
info shark;
int result = 0;

bool inRange(int x, int y) {
	return (0 <= x && x < 4 && 0 <= y && y < 4);
}

vector<vector<fish>> copy() {

	vector<vector<fish>> repos(4, vector<fish>(4));

	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++) 
				repos[i][j] = map[i][j]; 		
	return repos;
}

void restore(vector<vector<fish>> repos) {
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			map[i][j] = repos[i][j];
}

void move(vector<pair<int, int>> &fishInfo, info shark, int num) { 
//한 마리의 물고기에 대한 처리
//실시간으로 fishInfo를 업데이트 해야 하므로 참조 연산자 사용

	int x = fishInfo[num].first;
	int y = fishInfo[num].second;
	int dir = map[x][y].dir;

	for (int i = 0; i < 8; i++) { //반 시계 방향으로 방향 전환

		int next_dir = (dir + i) % 8;
		int next_x = x + direction[next_dir].first;
		int next_y = y + direction[next_dir].second;
		
		if (inRange(next_x, next_y) && !(next_x == shark.x && next_y == shark.y)) { //이동가능 하다면 swap
			
			fish tmp = map[next_x][next_y];
			map[next_x][next_y] = { num, next_dir }; 
			map[x][y] = tmp;
			fishInfo[num] = { next_x, next_y };
			fishInfo[tmp.num] = { x,y };

			break; // 물고기 이동 후 종료
		}
	}
}

void moveAll(info &shark) { // 전체 물고기에 대해

	vector<pair<int, int>> fishInfo(17, {-1,-1}); //n번 물고기와 좌표를 매핑

	for (int i = 0; i < 4; i++) 
		for (int j = 0; j < 4; j++) 
			if (!(i == shark.x && j == shark.y))
				if (map[i][j].num != 0) 
					fishInfo[map[i][j].num] = { i,j }; 
	
	for (int i = 1; i < 17; i++) 
		if (fishInfo[i].first != -1) 
			move(fishInfo, shark, i); //1번 부터 1마리 씩 이동
}

bool goHome(info &shark) { 

	int x = shark.x;
	int y = shark.y;

	while (inRange(x, y)) {  //범위 안에서 물고기가 있는지 확인
		
		if (map[x][y].num != 0)
			return false;
		x += direction[shark.dir].first;
		y += direction[shark.dir].second;
	}
	return true;
}

void solve(info shark) { 

	//repos를 전역변수로 사용한다면 다른 dfs에서 repos의 상태를 변화시킬 수 있으므로 불가능
	vector<vector<fish>> repos = copy();  
	moveAll(shark); // 물고기 전부 이동

	if (goHome(shark)) { //먹을 먹이가 없다면
		result = max(shark.cost, result); //결과 갱신
		restore(repos); //map복원
		return;
	}
	else {

		int x = shark.x + direction[shark.dir].first;
		int y = shark.y + direction[shark.dir].second;

		while (inRange(x, y)) {

			if (map[x][y].num != 0) {
				fish tmp = map[x][y];
				map[x][y] = { 0, 0 }; //해당 좌표의 물고기를 먹음
				solve({ x, y, tmp.dir, shark.cost + tmp.num }); //next
				map[x][y] = tmp; //back
			}
			x += direction[shark.dir].first;
			y += direction[shark.dir].second;
		}
		restore(repos); //map 복원
	}
}

int main() {

	ios_base::sync_with_stdio(false);
	cout.tie(0);
	cin.tie(0);

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			int num, dir;
			cin >> num >> dir;
			map[i][j] = { num, dir - 1 };
		}
	}

	shark = { 0,0, map[0][0].dir, map[0][0].num };
	map[0][0] = { 0,0 };
	result = shark.cost;
	solve(shark);

	cout << result << "\n";
}

문제 링크


www.acmicpc.net/problem/20061

 

20061번: 모노미노도미노 2

모노미노도미노는 아래와 같이 생긴 보드에서 진행되는 게임이다. 보드는 빨간색 보드, 파란색 보드, 초록색 보드가 그림과 같이 붙어있는 형태이다. 게임에서 사용하는 좌표 (x, y)에서 x는 행,

www.acmicpc.net

 

접근 방법


 한 보드에서 2점을 한번에 획득하는 경우를 충분히 고려하지 못하여 반례를 찾는데 시간이 좀 걸린 문제다.  

 

문제에 주어진 순서대로 구현을 하면된다.

1. 블럭 정보를 입력받는다.

2. 블럭 정보에 따라 파란보드와 초록보드에 표시한다.

3. 파란, 초록 보드를 각각 스캔하며 점수를 구하고 업데이트 한다.

 

1. 블럭 정보를 입력받는다 -> 블럭을 생성

vector<coord> constructor(int t, int x, int y) {

	vector<coord> block;

	if (t == 1)
		block.push_back({ x, y });
	else if (t == 2) {
		block.push_back({ x, y });
		block.push_back({ x, y + 1 });
	}
	else {
		block.push_back({ x, y });
		block.push_back({ x + 1, y });
	}
	return block;
}

 

2. 블럭 정보에 따라 각각의 보드에 표시한다.(파란 보드의 경우)

void moveToBlue(vector<coord> block) {

	bool ismove = true;

	while (ismove) {
		for (coord &elem : block) //이동
			elem.y += 1;
		for (coord elem : block) 
			if ( 9 < elem.y + 1 || mono[elem.x][elem.y + 1])
				ismove = false;
	}
	for (coord elem : block)
		mono[elem.x][elem.y] = true;
}

 

3. 보드를 각각 스캔하며 점수를 구하고 업데이트 한다.(blue board의 경우)

열이 꽉 차서 점수를 얻을 수 있을 때 열을 제거한 후 열을 가리키는 포인터를 유지해야 한다. 

int scanBlue() {

	int point = 0;

	for (int y = 9; y >= 6; y--) {
		int isFull = 0;
		for (int x = 0; x < 4; x++) {
			if (mono[x][y])
				isFull++;
		}
		if (isFull == 4) { //열이 꽉 찼다면
			deleteCol(y);  //열을 제거
			moveToBlueEnd(y); // 열의 오른쪽 부분을 왼쪽으로 모두 1칸 이동
			point++; //점수 증가
			y++;  // 제거 했다면 열을 가리키는 포인터를 유지시킴!! 
		}
	}

	for (int y = 4; y < 6; y++) {  // in Special Space
		for (int x = 0; x < 4; x++)
			if (mono[x][y]) {     //특별한 공간에 블럭이 포함됬다면
				deleteCol(9);     // 마지막 열 제거
				moveToBlueEnd(9);  // 마지막 열의 오른쪽 부분을 왼쪽으로 모두 1칸 이동
				break;
			}
	}
	return point;
}

 

3-1 deleteCol은 해당 열을 모두 지우는 함수이다.

void deleteCol(int col) {  //열
	for (int x = 0; x < 4; x++)
		mono[x][col] = false;
}

 

3-2 moveToBlueEnd(int col) -> col의 오른쪽 부분을 왼쪽으로 모두 1칸 이동시키는 함수 

void moveToBlueEnd(int col) {

	for (int y = col - 1; y > 3; y--) {
		for (int x = 0; x < 4; x++) {
			mono[x][y + 1] = mono[x][y];
			mono[x][y] = false;
		}
	}
}

 

 

소스 코드


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

using namespace std;

typedef struct coord {
	int x, y;
};

bool mono[10][10];
int ans = 0;
int N;

vector<coord> constructor(int t, int x, int y) {

	vector<coord> block;

	if (t == 1)
		block.push_back({ x, y });
	else if (t == 2) {
		block.push_back({ x, y });
		block.push_back({ x, y + 1 });
	}
	else {
		block.push_back({ x, y });
		block.push_back({ x + 1, y });
	}
	return block;
}

void moveToBlue(vector<coord> block) {

	bool ismove = true;

	while (ismove) {
		for (coord &elem : block) //이동
			elem.y += 1;
		for (coord elem : block) 
			if ( 9 < elem.y + 1 || mono[elem.x][elem.y + 1])
				ismove = false;
	}
	for (coord elem : block)
		mono[elem.x][elem.y] = true;
}

void moveToGreen(vector<coord> block) {

	bool ismove = true;

	while (ismove) {
		for (coord &elem : block)
			elem.x += 1;
		for (coord elem : block)
			if ( 9 < elem.x + 1 || mono[elem.x + 1][elem.y])
				ismove = false;
	}
	for (coord elem : block)
		mono[elem.x][elem.y] = true;
}

void move(vector<coord> &block) {
	moveToGreen(block);
	moveToBlue(block);
}

void deleteRow(int row) {  //행
	for (int y = 0; y < 4; y++)
		mono[row][y] = false;
}

void deleteCol(int col) {  //열
	for (int x = 0; x < 4; x++)
		mono[x][col] = false;
}

void moveToBlueEnd(int col) {

	for (int y = col - 1; y > 3; y--) {
		for (int x = 0; x < 4; x++) {
			mono[x][y + 1] = mono[x][y];
			mono[x][y] = false;
		}
	}
}

void moveToGreenEnd(int row) {

	for (int x = row - 1; x > 3; x--) {
		for (int y = 0; y < 4; y++) {
			mono[x + 1][y] = mono[x][y];
			mono[x][y] = false;
		}
	}
}

int scanBlue() {

	int point = 0;

	for (int y = 9; y >= 6; y--) {
		int isFull = 0;
		for (int x = 0; x < 4; x++) {
			if (mono[x][y])
				isFull++;
		}
		if (isFull == 4) {
			deleteCol(y);
			moveToBlueEnd(y);
			point++;
			y++;
		}
	}

	for (int y = 4; y < 6; y++) {  // in Special Space
		for (int x = 0; x < 4; x++)
			if (mono[x][y]) {
				deleteCol(9);
				moveToBlueEnd(9);
				break;
			}
	}
	return point;
}

int scanGreen() {

	int point = 0;

	for (int x = 9; x >= 6; x--) {
		int isFull = 0;
		for (int y = 0; y < 4; y++) {
			if (mono[x][y])
				isFull++;
		}
		if (isFull == 4) {
			deleteRow(x);
			moveToGreenEnd(x);
			point++;
			x++;
		}
	}

	for (int x = 4; x < 6; x++) {  // in Special Space
		for (int y = 0; y < 4; y++)
			if (mono[x][y]) {
				deleteRow(9);
				moveToGreenEnd(9);
				break;
			}
	}
	return point;
}

int scan() {
	
	int point = 0;
	
	point += scanBlue();
	point += scanGreen();
	
	return point;
}

int solve() {

	int t, x, y, point;
	cin >> t >> x >> y;

	vector<coord> block = constructor(t, x, y);
	move(block);
	point = scan();

	return point;
}

int count() {

	int cnt = 0;

	for (int y = 6; y < 10; y++)
		for (int x = 0; x < 4; x++)
			if (mono[x][y])
				cnt++;

	for (int x = 6; x < 10; x++)
		for (int y = 0; y < 4; y++)
			if (mono[x][y])
				cnt++;

	return cnt;
}

int main() {

	ios_base::sync_with_stdio(false);
	cout.tie(0);
	cin.tie(0);

	cin >> N;

	for (int i = 0; i < N; i++) 
		ans += solve();
		
	cout << ans << "\n";
	cout << count();
}

개발 환경 : VS2017

질문, 지적 환영합니다!

문제 링크


www.acmicpc.net/problem/17472

 

17472번: 다리 만들기 2

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.

www.acmicpc.net

 

접근 방법


 알고리즘적으로 어렵다기 보다 구현할 부분들이 많아 시간이 오래 걸렸던 문제이다. 

 

 일단 모든 섬이 연결되어야 어떠한 답을 도출할 수 있다. 모든 섬을 최소 비용으로 연결한다는 부분에서 MST(최소신장트리)를 떠올릴 수 있다. 트리의 특성 중 하나는 간선의 수는 노드 수보다 하나 적다는 것이다. 이러한 조건을 사용하여 모든 섬이 연결되어 있는지를 알 수 있다. 

 

 그러면 섬과 섬 사이의 거리를 알아야 한다. 이런 사전작업은 BruteForce의 방법으로 해결을 하였다. 즉 단순무식하게 경우의 수를 모두 검사하였다.  

 

 그렇다면 섬과 섬사이의 거리를 구하기 위해서는 (1)섬의 정보들을 저장하고 (2)world[N][M]를 먼저 갱신하는 작업을 해야한다. 

 

(1)과 (2)의 작업을 거치게 된다면 이렇게 될것이다.

※하단 소스코드 BFS(int x, int y, int num)와 islandInfo() 함수 참고

 

 

여기서 갱신된 지도를 world[N][M]이라 하고, 섬의 정보들을 갖춘 2차원 벡터를 islands라 하였을 때, 이 두 가지의 정보로 섬과 섬사이의 거리를 구할 수 있다. 

 

 islands의 한 섬을 island라 하였을 때 island를 이루고 있는 모든 좌표에 대해 상하좌우 4방향으로 검사를 해야한다. 

어떠한 섬에 도달할 수 있다면 다리의 길이 값을 계속해서 최소로 갱신하여 준다.

 

 갱신이 완료되면 크루스칼 알고리즘을 적용하기 위해 비용을 기준으로 오름차순 정렬한다.

 

※하단 소스코드 islandConnect(vector<vector<pair<int, int>>> islands)와 go(pair<int, int> coord, int dir) 참고 

 

 

이제 정렬된 정보를 바탕으로 크루스칼 알고리즘을 적용하여 간선의 수를 count하는 동시에 다리 길이의 합도 구해준다. 최종적으로 count수가 섬의 수보다 1적다면 정답을 아니라면 -1을 return한다.

 

소스 코드


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

using namespace std;

int world[100][100];
pair<int, int> direction[4] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
bool visited[100][100];
int N, M;

typedef struct info {
	int x;
	int y;
	int cost;
};

void printAll(vector<info> connectInfo, vector<vector<pair<int, int>>> islands) {
//문제 풀다가 출력할 일 있으면 사용하세요! 
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			cout << world[i][j] << " ";
		}
		cout << "\n";
	}
	cout << "\n";
	for (int i = 1; i < islands.size(); i++) {
		cout << "islands num: " << i << "\n";
		for (int j = 0; j < islands[i].size(); j++) {
			cout << "[" << islands[i][j].first << ", " << islands[i][j].second << "] ";
		}
		cout << "\n";
	}

	cout << "\n";
	for (int i = 0; i < connectInfo.size(); i++)
		cout << "island1: " << connectInfo[i].x << " island2: " << connectInfo[i].y << " length: " << connectInfo[i].cost << endl;
}

bool inRange(int x, int y) {
	return (0 <= x && x < N && 0 <= y && y < M);
}

bool comp(info &a, info &b) {
	return a.cost < b.cost;
}

vector<pair<int, int>> BFS(int x, int y, int num) {

	vector<pair<int, int>> retV;
	queue<pair<int, int>> q;

	retV.push_back({ x,y });
	q.push({ x,y });
	visited[x][y] = true;
	world[x][y] = num;

	while (!q.empty()) {
		
		int now_x = q.front().first;
		int now_y = q.front().second;
		q.pop();

		for (int i = 0; i < 4; i++) {
			int next_x = now_x + direction[i].first;
			int next_y = now_y + direction[i].second;

			if (inRange(next_x, next_y) && !visited[next_x][next_y] && world[next_x][next_y] != 0) {
				q.push({ next_x, next_y });
				retV.push_back({ next_x, next_y });
				visited[next_x][next_y] = true;
				world[next_x][next_y] = num;
			}
		}
	}

	return retV;
}

vector<vector<pair<int, int>>> islandInfo() {

	vector<vector<pair<int, int>>> islands;
	int islandNum = 1;
	islands.push_back({ {-1,-1} }); //섬의 번호는 1부터 -> index와 섬의 번호를 일치 시킴

	for (int i = 0; i < N; i++) 
		for (int j = 0; j < M; j++) 
			if (world[i][j] != 0 && !visited[i][j]) {
				vector<pair<int, int>> island = BFS(i, j, islandNum);
				islands.push_back(island);
				islandNum++;
			}

	return islands;
}


info go(pair<int, int> coord, int dir) {

	int x = coord.first;
	int y = coord.second;
	int from = world[x][y];
	int to = from;
	int len = 0;

	while (true) {

		x += direction[dir].first;
		y += direction[dir].second;
		
		if (inRange(x, y)) {
			if (world[x][y] == 0)
				len++;
			else if (world[x][y] == from)
				break;
			else {
				to = world[x][y];
				break;
			}
		}
		else
			break;
	}
	return { from, to, len };
}


vector <info> islandConnect(vector<vector<pair<int, int>>> islands) {

	map <pair<int, int>, int> costInfo;
	vector <info> retV;

	for (int num = 1; num < islands.size(); num++) {
		vector<pair<int, int>> island = islands[num];
		for (int i = 0; i < island.size(); i++) {
			pair<int, int> coord = island[i];
			for (int dir = 0; dir < 4; dir++) {

				info state = go(coord, dir);
				
				if (state.cost < 2 || state.y == state.x) 
					continue;
				else {
					if (state.x < state.y) { //간선은 양방향 이므로 중복되는 정보를 저장하지 않음
						if (costInfo.count({ state.x, state.y }) == 0)
							costInfo[{ state.x, state.y }] = state.cost;
						else
							costInfo[{ state.x, state.y }] = min(costInfo[{ state.x, state.y }], state.cost);
					}
				}
			}
		}
	}

	for (pair<pair<int, int>, int> cost : costInfo)
		retV.push_back({cost.first.first, cost.first.second, cost.second });
	sort(retV.begin(), retV.end(), comp);

	return retV;
}

int find(int a, vector<int> &parent) {
	
	if (parent[a] == a)
		return a;
	if (parent[a] == -1)
		parent[a] = a;

	return parent[a] = find(parent[a], parent);
}

bool Union(int a, int b, vector<int> &parent) {

	a = find(a, parent);
	b = find(b, parent);

	if (a != b) {
		parent[b] = a;
		return true;
	}
	return false;
}

int solve() {

	int result = 0;
	int cnt = 0;

	vector<vector<pair<int, int>>> islands = islandInfo();
	vector <info> connectInfo = islandConnect(islands);
	vector <int> parent(islands.size(), -1); 
    
    //정렬된 정보를 바탕으로 크루스칼 알고리즘 실행
	for (int i = 0; i < connectInfo.size(); i++) { 
		if (Union(connectInfo[i].x, connectInfo[i].y, parent)) {
			cnt++;
			result += connectInfo[i].cost;
		}
	}
	
	//printAll(connectInfo, islands);
    //islands의 index와 섬의 번호를 강제로 일치시켜놓았기 때문에 size는 섬의 수보다 1더 크다.
	return result = (cnt == islands.size() - 2) ? result : -1; 
}

int main() {

	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin >> N >> M;
	
	for (int i = 0; i < N; i++) 
		for (int j = 0; j < M; j++) 
			cin >> world[i][j];
		
	cout << solve();

}

개발 환경 : VS2017

질문, 지적 환영합니다!

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 19236 청소년 상어  (0) 2021.01.06
[C++] 백준 20061 모노미노도미노  (0) 2021.01.05
[C++] 백준 17281 야구  (0) 2021.01.04
[C++] 백준 17406 배열돌리기4  (0) 2021.01.03
[C++] 백준 3190 뱀  (0) 2021.01.03

문제 링크


www.acmicpc.net/problem/17281

 

17281번: ⚾

⚾는 9명으로 이루어진 두 팀이 공격과 수비를 번갈아 하는 게임이다. 하나의 이닝은 공격과 수비로 이루어져 있고, 총 N이닝 동안 게임을 진행해야 한다. 한 이닝에 3아웃이 발생하면 이닝이 종

www.acmicpc.net

 

접근 방법


문제 풀이보다 문제를 이해하는데 어려운 문제였다.

 

문제를 간략히 정리하자면

1. 타자들의 순번을 정한다.

2. 1번 선수는 항상 4번째 순서에 와야한다.

3. 아웃이 3번 될 때까지 현재 순열을 반복하여 점수를 더한다.

4. 아웃이 3번 된다면 다음 타자의 순번을 구한다.

5. 모든 이닝이 끝날 때 까지 반복하여 게임의 총 점수를 구한다.

6. 점수의 최대 값을 갱신하며 위의 과정을 반복한다.

 

코드를 단계 별로 보자면

1. DFS를 통해 순열을 구한다. 이 때 cnt=3(4번 타자)는 고정이므로 pass하고, 순열이 완성 되었을 때 게임의 점수를 구하는 play_game()함수를 실행한다.

int solve(int cnt) {

	int maximum = 0;

	if (cnt == 3) //4번 타자는 pass
		cnt++;
	if (cnt == 9) {
		return play_game();
	}
	for (int i = 0; i < 9; i++) {
		if (!visited[i]) { //방문 여부
			visited[i] = true;
			seq[cnt] = i;
			maximum = max(maximum, solve(cnt + 1));
			visited[i] = false;
		}
	}
	return maximum;
}

2. while문을 통해 한 이닝이 끝났을 때 점수(sum)를 더해주며, 다음 이닝의 선두주자(now)를 갱신한다. 

int play_game() { //순서가 결정 되었을 때

	int inning = -1;
	int now = 0;
	int sum = 0;

	while (++inning < N) {
		pair<int, int> info = oneInning(inning, now);
		sum += info.first;
		now = info.second;
	}
	return sum;
}

3. base는 홈 부터 1,2,3루를 의미하며, 아웃(info[inning][hitter] == 0)인 경우 변수out을 증가시켜 반복한다. out이 아니라면 각각의 타자가 친 정보를 통해 move()함수를 실행하여 base를 갱신하며 점수를 구한다.

pair<int, int> oneInning(int inning, int idx) { 
	
	int out = 0;
	int score = 0;

	vector<bool> base = {1,0,0,0};

	while(out < 3) {

		int hitter = seq[idx];
		base[0] = true; //타자 투입

		if (info[inning][hitter] == 0)
			out++;
		else
			score += move(base, info[inning][hitter]);

		idx = (idx + 1) % 9;
	}
		
	return { score, idx };

}

4. 타자의 n루타의 정보를 통해 점수를 얻을 수 있는 상황(next >= 4)에는 score를 획득하며, 아닌 경우에는 루의 위치를 변경시켜준다. 

int move(vector<bool> &base, int scoretype) { 

	int score = 0;

	for (int i = 3; i >= 0; i--) {
		if (base[i]) {
			int next = i + scoretype;
			if (next >= 4) {
				score++;
				base[i] = false;
			}				
			else {
				base[next] = true;
				base[i] = false;
			}
		}
	}
	return score;
}

 

소스 코드


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

using namespace std;

bool visited[9];
int seq[10];
int info[51][9];
int N;

int move(vector<bool> &base, int scoretype) { 

	int score = 0;

	for (int i = 3; i >= 0; i--) {
		if (base[i]) {
			int next = i + scoretype;
			if (next >= 4) {
				score++;
				base[i] = false;
			}				
			else {
				base[next] = true;
				base[i] = false;
			}
		}
	}
	return score;
}

pair<int, int> oneInning(int inning, int idx) { 
	
	int out = 0;
	int score = 0;

	vector<bool> base = {1,0,0,0};

	while(out < 3) {

		int hitter = seq[idx];
		base[0] = true; //타자 투입

		if (info[inning][hitter] == 0)
			out++;
		else
			score += move(base, info[inning][hitter]);

		idx = (idx + 1) % 9;
	}
		
	return { score, idx };

}

int play_game() { //순서가 결정 되었을 때

	int inning = -1;
	int now = 0;
	int sum = 0;

	while (++inning < N) {
		pair<int, int> info = oneInning(inning, now);
		sum += info.first;
		now = info.second;
	}
	return sum;
}

int solve(int cnt) {

	int maximum = 0;

	if (cnt == 3)
		cnt++;
	if (cnt == 9) {
		return play_game();
	}
	for (int i = 0; i < 9; i++) {
		if (!visited[i]) {
			visited[i] = true;
			seq[cnt] = i;
			maximum = max(maximum, solve(cnt + 1));
			visited[i] = false;
		}
	}
	return maximum;
}

int main() {

	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin >> N;

	for (int i = 0; i < N; i++) 
		for (int j = 0; j < 9; j++) 
			cin >> info[i][j];

	visited[0] = true;
	seq[3] = 0;

	cout << solve(0);
}

개발 환경 : VS2017

질문, 지적 환영합니다!

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 20061 모노미노도미노  (0) 2021.01.05
[C++] 백준 17472 다리만들기2  (0) 2021.01.04
[C++] 백준 17406 배열돌리기4  (0) 2021.01.03
[C++] 백준 3190 뱀  (0) 2021.01.03
[C++] 백준 17136 색종이 붙이기  (0) 2021.01.02

문제 링크


www.acmicpc.net/problem/17406

 

17406번: 배열 돌리기 4

크기가 N×M 크기인 배열 A가 있을때, 배열 A의 값은 각 행에 있는 모든 수의 합 중 최솟값을 의미한다. 배열 A가 아래와 같은 경우 1행의 합은 6, 2행의 합은 4, 3행의 합은 15이다. 따라서, 배열 A의

www.acmicpc.net

 

접근 방법


 처음에 문제를 해결하고 나서 왜맞틀을 시전했던 문제입니다. 문제를 잘 못 이해해서 생긴 문제인데 문제에서 배열의 회전 명령어를 순차적으로 실행하는 것이 아닌 주어진 명령어의 모든 조합을 다 실행하여 그 중 최소를 구하는 문제였습니다. 

 

문제 풀이의 순서는 다음과 같습니다.

1. 가능한 명령의 조합을 구한다.

2. 한 조합에서 한 명령어를 실행한다.

3. 한 명령어를 실행한다.

4. 한 명령어에 s~1까지 순차적으로 배열을 회전 시킨다.

5. 한 조합의 명령어를 모두 실행시킨 후 배열의 값을 구한다.

6. 다음 조합도 위의 과정을 실행하며 최솟값을 갱신한다.

 

소스 코드


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

using namespace std;

typedef struct info {
	int r; int c; int s;
};

vector <vector<int>> map;
vector <info> inst;
int N, M, K;

void init() {
	
	cin >> N >> M >> K;

	for (int i = 0; i < N; i++) {
		vector<int> v;
		for (int j = 0; j < M; j++) {
			int num; cin >> num;
			v.push_back(num);
		}
		map.push_back(v);
	}
	for (int i = 0; i < K; i++) {
		int r, c, s;
		cin >> r >> c >> s;
		inst.push_back({ r - 1, c - 1, s });
	}
}

vector<vector<int>> sequence(vector<int> &seq, vector<vector<int>> &seqs, vector<bool> &visited) {

	if (seq.size() == inst.size()) {
		seqs.push_back(seq);
		return seqs;
	}
	for (int i = 0; i < inst.size(); i++) {
		if (!visited[i]) {
			visited[i] = true;
			seq.push_back(i);
			sequence(seq, seqs, visited);
			visited[i] = false;
			seq.pop_back();
		}
	}
	return seqs;
}

int findMin(vector<vector<int>> newmap) {
	
	int minimum = 0xFFFFFFF;

	for (int i = 0; i < N; i++) {
		int sum = 0;
		for (int j = 0; j < M; j++)
			sum += newmap[i][j];
		minimum = min(sum, minimum);
	}
	return minimum;
}

void rotate(int x, int y, int dist, vector<vector<int>> &ret) {

	vector<vector<int>> tmp = ret;

	for (int i = y - dist; i < y + dist; i++) 
		ret[x - dist][i + 1] = tmp[x - dist][i]; //높이 고정
	for (int i = x - dist; i < x + dist; i++)
		ret[i + 1][y + dist] = tmp[i][y + dist];  // 축 고정
	for (int i = y + dist; i > y - dist; i--)
		ret[x + dist][i - 1] = tmp[x + dist][i]; //높이 고정
	for (int i = x + dist; i > x - dist; i--)
		ret[i - 1][y - dist] = tmp[i][y - dist];  // 축 고정
}

int processing(vector<int> seq) { // 한 명령어 집합에 대한 것
	
	vector<vector<int>> newmap = map;

	for (int i = 0; i < seq.size(); i++) {

		int r = inst[seq[i]].r;
		int c = inst[seq[i]].c;
		int s = inst[seq[i]].s;

		for (int j = 1; j <= s; j++)
			rotate(r, c, j, newmap);
	}
	return findMin(newmap);
}

int solve() {
	
	vector <vector<int>> seqs;
	vector <int> seq;
	vector <bool> visited(6, false);
	int minimum = 0xFFFFFFF;

	seqs = sequence(seq, seqs, visited);

	for (int i = 0; i < seqs.size();i++) 
		minimum = min(minimum, processing(seqs[i]));
	
	return minimum;
}

int main() {
	
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	init();

	cout << solve() << "\n";
}

개발 환경 : VS2017

질문, 지적 환영합니다!

 

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 17472 다리만들기2  (0) 2021.01.04
[C++] 백준 17281 야구  (0) 2021.01.04
[C++] 백준 3190 뱀  (0) 2021.01.03
[C++] 백준 17136 색종이 붙이기  (0) 2021.01.02
[C++] 백준 9470 Strahler 순서  (0) 2020.09.25

문제 링크


www.acmicpc.net/problem/3190

 

3190번: 뱀

 'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임

www.acmicpc.net

 

접근 방법


 deque의 자료구조를 사용하여 어렵지 않게 구현 할 수 있는 문제였습니다. 매 시간마다 현재의 방향을 설정해줍니다. 현재 시간에 방향을 바꿔야하는 명령어가 있다면 방향을 전환합니다. 그 다음 설정된 방향으로 이동을 했을 때 4가지를 검사합니다. 

 

1. 다음 위치가 사과인지

2. 다음 위치가 뱀의 몸인지

3. 다음 위치가 벽인지(범위를 벗어났는지)

4. 다음 위치가 빈 공간인지 

 

문제에서 제시된 대로 이동에 대한 규칙은 머리를 먼저 추가합니다. 

1의 경우라면 더 이상의 처리는 없습니다.

2의 경우라면 게임을 종료합니다.

3의 경우라면 게임을 종료합니다.

4의 경우라면 가장 말단의 꼬리를 제거합니다.

 

 

소스 코드


#include <iostream>
#include <vector>
#include <deque>
#include <queue>
#define body 1
#define apple 2
#define wall 3

using namespace std;

typedef struct coord {
	int x;
	int y;
};

deque <coord> snake;
queue <pair<int, char>> inst;
coord direction[4] = { {0,1}, {1,0}, {0,-1}, {-1,0} };
int N, K, L;
int map[103][103];

bool inRange(int x, int y) {
	return (0 < x && x <= N && 0 < y && y <= N) ? true : false;
}

int nextSquare(coord head, int dir) {
	
	coord nextcoord = { head.x + direction[dir].x, head.y + direction[dir].y };

	if (map[nextcoord.x][nextcoord.y] == apple)
		return apple;
	else if (map[nextcoord.x][nextcoord.y] == body)
		return body;
	else if (!inRange(nextcoord.x, nextcoord.y))
		return wall;
	else
		return 0;
}

int change_dir(int dir, char inst) {

	dir += 4;
	if (inst == 'L')
		return (dir - 1) % 4;
	else
		return (dir + 1) % 4;
}

int now_dir(int cnt, int dir) {

	int newdir = dir;

	if (!inst.empty() && inst.front().first == cnt) {
		newdir = change_dir(dir, inst.front().second);
		inst.pop();
	}
	return newdir;
}

bool go(int dir) {
	
	coord head = snake.back();
	
	int nextState = nextSquare(head, dir);
	coord nextcoord = { head.x + direction[dir].x, head.y + direction[dir].y };
	
	snake.push_back(nextcoord);
	map[snake.back().x][snake.back().y] = body;

	if (nextState == apple) {
		return true;
	}
	else if (nextState == body) {
		return false;
	}
	else if (nextState == wall) {
		return false;
	}
	else {
		map[snake.front().x][snake.front().y] = 0;
		snake.pop_front();
		return true;
	}

}

int solve() {

	int cnt = 0;
	int dir = 0;
	
	snake.push_back({ 1,1 });
	map[1][1] = body;
	
	while (true) {
		//printmap();
		dir = now_dir(cnt, dir);
		cnt++;
		if (!go(dir))
			break;
	}
	return cnt;
}

int main() {
	
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	cin >> N;

	cin >> K;
	for (int i = 0; i < K; i++) {
		int x, y;
		cin >> x >> y;
		map[x][y] = apple;
	}

	cin >> L;
	for (int i = 0; i < L; i++) {
		int t; char dir;
		cin >> t >> dir;
		inst.push({ t, dir });
	}

	cout << solve() << endl;

}

개발 환경 : VS2017

질문, 지적 환영합니다!

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 17281 야구  (0) 2021.01.04
[C++] 백준 17406 배열돌리기4  (0) 2021.01.03
[C++] 백준 17136 색종이 붙이기  (0) 2021.01.02
[C++] 백준 9470 Strahler 순서  (0) 2020.09.25
[C++] 백준 17070 파이프 옮기기 1  (0) 2020.09.18

+ Recent posts