문제 링크


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

문제 링크


www.acmicpc.net/problem/17136

 

17136번: 색종이 붙이기

<그림 1>과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는 1×1, 2×2, 3×3, 4×4, 5×5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다. <그림 1> 색종이를 크

www.acmicpc.net

 

접근 방법


 DFS로 접근하여 풀 수 있는 문제였으나 시간 초과를 해결하는 과정이 관건이었던 문제였다.

전체 공간인 map[10][10]을 탐색하면서 색종이를 붙여야 되는 공간이 있다면 큰 색종이 부터 시작하여 붙일 수 있는지에 대한 여부를 확인하고 색종이를 붙인다. 완전탐색으로 가장 적은 색종이를 사용하는 경우의 값을 구하면 된다. 

 

 시간초과를 해결하는 방법을 떠올리기가 쉽지가 않았는데 핵심은 현재 공간에 어떤 색종이도 만족하지 않는다면 그 다음 과정에서 이러한 공간을 어차피 만족시킬 수 없다는 것이다. 즉 현재 공간을 만족시킬 수 없다면 다음 과정으로 진행을 시키지 않는 것이 시간초과를 해결하는 핵심이었다.

int solve(int x, int y, int cnt) {

	if (complete()) 
		return cnt;
	if (mincnt <= cnt)
		return mincnt;

	for (int i = x; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			if (map[i][j] == 1) {
				for (int size = 5; size >= 1; size--) {
					if (inRange(i, j, size)) {
						if (check_map(i, j, size) && cntPerSize[size] > 0) {
							cntPerSize[size]--;
							update_map(i, j, size, 0);							
							mincnt = min(solve(i, j, cnt + 1), mincnt);
							cntPerSize[size]++;
							update_map(i, j, size, 1);
						}
					}
				}
				return mincnt; // !!!중요: 위의 과정에서 만족하지 않는다면 return시킴
			}
		}
	}

	return mincnt;
}

 

소스 코드


#include <iostream>
#include <algorithm>

using namespace std;

int map[10][10];
int cntPerSize[6] = { 0,5,5,5,5,5 };
int mincnt = 99999;

bool inRange(int x, int y, int size) {
	return (x + size - 1 < 10 && y + size - 1 < 10) ? true : false;
}

void update_map(int x, int y, int size, int action) {
	for (int i = x; i < x + size; i++)
		for (int j = y; j < y + size; j++)
			map[i][j] = action;
}

bool check_map(int x, int y, int size) {
	for (int i = x; i < x + size; i++)
		for (int j = y; j < y + size; j++)
			if (map[i][j] == 0)
				return false;
	return true;
}

bool complete() {
	for (int i = 0; i < 10; i++)
		for (int j = 0; j < 10; j++)
			if (map[i][j] == 1)
				return false;
	return true;
}

int solve(int x, int y, int cnt) {

	if (complete()) 
		return cnt;
	if (mincnt <= cnt)
		return mincnt;

	for (int i = x; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			if (map[i][j] == 1) {
				for (int size = 5; size >= 1; size--) {
					if (inRange(i, j, size)) {
						if (check_map(i, j, size) && cntPerSize[size] > 0) {
							cntPerSize[size]--;
							update_map(i, j, size, 0);							
							mincnt = min(solve(i, j, cnt + 1), mincnt);
							cntPerSize[size]++;
							update_map(i, j, size, 1);
						}
					}
				}
				return mincnt;
			}
		}
	}

	return mincnt;
}

int main() {

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

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

	int ans = solve(0,0,0);
	if (ans < 99999)
		cout << ans;
	else
		cout << -1;
}

개발 환경 : VS2017

질문, 지적 환영합니다!

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

[C++] 백준 17406 배열돌리기4  (0) 2021.01.03
[C++] 백준 3190 뱀  (0) 2021.01.03
[C++] 백준 9470 Strahler 순서  (0) 2020.09.25
[C++] 백준 17070 파이프 옮기기 1  (0) 2020.09.18
[C++] 백준 17135 캐슬디펜스  (0) 2020.09.18

문제 링크


www.acmicpc.net/problem/9470

 

9470번: Strahler 순서

지질학에서 하천계는 유향그래프로 나타낼 수 있다. 강은 간선으로 나타내며, 물이 흐르는 방향이 간선의 방향이 된다. 노드는 호수나 샘처럼 강이 시작하는 곳, 강이 합쳐지거나 나누어지는 곳

www.acmicpc.net

접근 방법


 최근 위상 정렬 문제들을 많이 풀면서 대부분 동적계획법의 접근 방법으로 해결이 되어 동적 계획법으로 해결하였다. 

위상을 나타내는 그래프는 vector<int> topology[size]로 나타내었으며, topology[to].push_back(from)으로 입력받아 현제 노드가 어디로부터 왔는지의 정보로 기록하였다.

 

int solve(int now) {

	int &ref = dp[now];
	int number = 0;
	int cnt = 0;

	if (ref != 0) 
		return ref;
	if (topology[now].size() == 0)
		return ref = 1;

	for (int i = 0; i < topology[now].size(); i++) {
		int from = topology[now][i];
		int ret = solve(from);
		if (number < ret) {
			cnt = 1;
			number = ret;
		}
		else if (number == ret) 
			cnt++;
	}

	if (cnt >= 2)
		number += 1;
	return ref = number;
}

 

number로 가장 큰 Strahler순서를 찾았으며, 가장 큰 Strahler순서가 유입된 횟수를 cnt로 카운트하였다.   

 

소스 코드


#include <iostream>
#include <algorithm>
#include <queue>
#include <string.h>
#include <vector>

using namespace std;

vector<int> topology[1001];
int dp[1001];
int t, k, m, p;

void init() {
	memset(dp, 0, sizeof(dp));
	for (int i = 0; i <= m; i++) 
		topology[i].clear();
}

int solve(int now) {

	int &ref = dp[now];
	int number = 0;
	int cnt = 0;

	if (ref != 0) 
		return ref;
	if (topology[now].size() == 0)
		return ref = 1;

	for (int i = 0; i < topology[now].size(); i++) {
		int from = topology[now][i];
		int ret = solve(from);
		if (number < ret) {
			cnt = 1;
			number = ret;
		}
		else if (number == ret) 
			cnt++;
	}

	if (cnt >= 2)
		number += 1;
	return ref = number;
}

int main() {

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

	cin >> t;
	while (--t >= 0) {
		cin >> k >> m >> p;
		init();
		for (int i = 0; i < p; i++) {
			int from, to;
			cin >> from >> to;
			topology[to].push_back(from);
		}
		cout << k << " " << solve(m) << "\n";
	}
}

개발 환경 : VS2017

질문, 지적 환영합니다!

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

[C++] 백준 3190 뱀  (0) 2021.01.03
[C++] 백준 17136 색종이 붙이기  (0) 2021.01.02
[C++] 백준 17070 파이프 옮기기 1  (0) 2020.09.18
[C++] 백준 17135 캐슬디펜스  (0) 2020.09.18
[C++] 백준 5670 휴대폰 자판  (0) 2020.09.10

+ Recent posts