본문 바로가기

코드포스_Code Forces/Code Forces (Div. 2)

[코드포스/Contest] Coderforces Round #648 (Div. 2)

 

클릭하면 코드포스 페이지로 이동합니다.


 

#648 후기

 

A부터 C번까지는 구현보다는 아이디어를 떠올리는게 중요했다고 생각이 드는데, 내가 이런 부분에서 약점을 가지고 있다는 것을 다시 알게 되었고, D번도 다른 방법이 있을 수 있겠지만, 구현능력이 좋았다면 풀 수 있었지 않았을까 하는 생각이 드는 내 한계를 느낀 아쉬운 대회였다.

 


 

#648 문제

  A번 Matrix Game - 성공

더보기

 

n * m 크기의 행렬이 있다. 행렬은 0 또는 1로 초기화 할 수 있다. matrix[i][j] == 1 이란 뜻은 해당 행렬에 i번째 가로줄과 j번째 세로줄에는 다른 1인 값이 들어올 수 없다는 것을 의미한다. Matrix Game은 Ashish와 Vivek이 서로 한 번 씩 행렬을 1로 바꿀 수 있을 때, 더이상 바꿀 수 없는 사람이 지는 게임이다. 행렬이 주어졌을 때, 이기는 사람을 출력하시오.

 

 단순히 문제에서 나온대로 구현하면 되는 문제이다.

 

나는 배열의 크기를 각각 (n + 1) * (m + 1)로 초기화 하고,

입력을 받을 때, block[i][0]과 block[0][j]에 각각 해당 가로, 세로줄의 사용 여부를 저장했다.

 

그리고 배열을 순회하면서, 가로와 세로 줄 모두 사용할 수 있으면, 해당 위치를 1로 바꾸고, 가로/세로줄을 사용하지 못하게 바꾸면서 누가 이겼는지 확인하였다.

 

정답 코드 보기

#include <bits/stdc++.h>
// 2의 제곱수 판정
#define POW2(X) (X) == ((X)&(-(X)))

// 기본설정
typedef long long ll;
using namespace std;
int dirx[4] = {1, 0, -1, 0};
int diry[4] = {0, 1, 0, -1};

int main(void) {
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);

	int tc;
	cin >> tc;

	while(tc--) {
		int n, m;
		bool turn = true;
		cin >> n >> m;
		int block[50 + 1][50 + 1] = {0};
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++) {
				cin >> block[i][j];
				if(block[i][j] == 1) {
					block[i][0] = 1;
					block[0][j] = 1;
				}
			}
		}
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++) {
				if(block[i][0] == 1 || block[0][j] == 1) continue;
				else {
					block[i][j] = 1;
					block[0][j] = 1;
					block[i][0] = 1;
					turn = !turn;
				}
			}
		}

		if(turn) {
			cout << "Vivek";
		} else {
			cout << "Ashish";
		}
		cout << "\n";
	}

	return 0;
}

  B번 Trouble Sort - 성공

더보기
두개의 값을 인자로 가지는 배열이 있다. 첫번째 값은 Value로, 정수 값이 주어진다. 두번째 값은 Type으로, 0과 1의 두가지로 주어진다. Type이 서로 다른 경우, 서로 자리를 바꿀 수 있다.
배열이 주어졌을 때, 해당 배열을 감소하지 않는 배열로 바꿀 수 있는지 출력하시오.

 

처음에 하나하나 다 해봐야하나 고민하다가, 한가지 특징을 발견하였다. 바로

 

배열에 Type이 0과 1 모두 존재한다면, 감소하지 않는 배열로 만들 수 있다는 것이다.

 

즉, Type이 1인 원소 하나와, Type이 0인 원소 나머지가 있으면, 1의 자리를 옮기면서 배열을 정렬할 수 있다.

 

그럼 다음으로 생각해야 할 것은, 둘 중에 하나라도 없을 경우이다.

 

하나라도 없을 경우, 기본 배열이 감소하지 않는다면 변화시키지 않아도 YES이고, 그렇지 않다면 NO이다.

 

정답 코드 보기

#include <bits/stdc++.h>
// 2의 제곱수 판정
#define POW2(X) (X) == ((X)&(-(X)))

// 기본설정
typedef long long ll;
using namespace std;
int dirx[4] = {1, 0, -1, 0};
int diry[4] = {0, 1, 0, -1};

int main(void) {
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);

	int tc;
	cin >> tc;

	while(tc--) {
		int n, temp, zero = 0, one = 0, prev = 0;
		bool ans = true;
		cin >> n;
		vector<int> a;
		for(int i = 0; i < n; i++) {
			cin >> temp;
			a.push_back(temp);
			if(prev == 0) prev = temp;
			else {
				if(prev <= temp) {
					continue;
				} else {
					ans = false;
				}
			}
		}
		for(int i = 0; i < n; i++) {
			cin >> temp;
			if(temp == 1) one++;
			else zero++;
		}
		if(one * zero == 0) {
			if(ans) cout << "YES\n";
			else cout << "NO\n";
		} else {
			cout << "YES\n";
		}
	}

	return 0;
}

C번 Rotation Matching - 성공

더보기
길이가 n이고, 각각의 원소는 1 ~ n인, 동일한 두 정수 배열 a, b가 주어진다.
a와 b의 각 원소에 대해서, 위치(index)와 값이 서로 같으면 두 페어는 매치된다고 본다.
두 배열을 서로 왼쪽 또는 오른쪽으로 밀 수 있을때, 최대로 매치되는 원소의 개수를 출력하시오.

 

문제를 다음과 같이 접근했다.

 

배열 a의 원소 a[i]와 배열 b의 원소 b[j]가 같다고 가정하면, 1 ~ n까지의 각각의 원소에 대해서,

b의 인덱스 j를 a의 인덱스 i로 바꾸기 위한 미는 횟수를 구해서 배열에 저장한다.

 

문제의 예제로 예를 들어 설명하면,

예제 1번

배열 b [2, 3, 4, 5, 1]에서 2를 a의 2의 위치로 이동시키기 위해서는 오른쪽으로 1번, 3을 a의 3의 위치로 이동시키기 위해서는 오른쪽으로 한번 , ... 이렇게 동일하게 진행해서,

 

오른쪽으로 한번 움직이면 5개의 원소를 일치시킬 수 있다고 볼 수 있다.

 

이와 같이 코드에서 각각의 원소에서 몇 번 움직여야 일치되는지 확인한 후, 마지막에 가장 많이 일치시킬 수 있는지 확인해서 출력해주었다. 

 

 정답 코드 보기 

#include <bits/stdc++.h>
// 2의 제곱수 판정
#define POW2(X) (X) == ((X)&(-(X)))

// 기본설정
typedef long long ll;
using namespace std;
int dirx[4] = {1, 0, -1, 0};
int diry[4] = {0, 1, 0, -1};

int main(void) {
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);
	int n, cnt = 0, val;
	cin >> n;
	int ans[n + 1];	// ans[x] : x번 움직였을 때, 일치하는 개수
	int a[n] = {0};		// a[x] == 1 : x라는 숫자가 1번째 index에 있음

	for(int i = 0; i < n; i++) {
		cin >> val;
		a[val] = i;
	}

	memset(ans, 0, sizeof(ans));

	for(int i = 0; i < n; i++) {
		cin >> val;
		// cout << ans[i] << " " << i <<"\n";
		if(i == a[val]) {
			ans[0]++;
		} else if(i < a[val]) {
			// a에서 현재 bi의 값이 더 오른쪽에 있을 경우
			ans[a[val] - i]++;
		} else {
			// 더 왼쪽에 있을 경우
			int idx = n - i + a[val];
			ans[idx]++;
		}
	}

	int maxnum = 0;
	for(int i = 0; i <= n; i++) {
		if(maxnum < ans[i]) maxnum = ans[i];
	}
	cout << maxnum;

	return 0;
}

D번 Solve The Maze - 실패

더보기
n * m 크기의 미로가 주어진다. 배열의 원소로는 빈 공간('.'), 벽('#'), 좋은사람('G'), 나쁜사람('B') 이 주어진다.
미로에서 탈출하는 유일한 방법은, (n, m) 좌표로 이동해서 탈출하는 것이다.
하지만 조건이 있다. G는 항상 탈출해야만 하고, B는 탈출해서는 안된다. 이를 위해, 원하는 위치에 벽('#')을 설치할 수 있다. 조건에 부합하여, G만이 탈출할 수 있는지를 출력하시오.

 

(대회중에는 구현하다가 시간이 부족해서 포기했는데, 다시 풀어서 성공하였다.)

 

접근 방법은 간단하다.

 

만약 미로에 G가 없으면, 모든 G가 탈출한것이므로, YES이다.

 

그렇지 않다면, B를 확인해서, B의 상/하/좌/우에 벽을 설치한다. 설치하는 중에, B와 G가 인접한것이 있다면, G가 탈출할 경우, B도 항상 탈출할 수 있기 때문에 NO이다.

또한 벽을 모두 설치한 이후에, 탈출구에 벽이 있다면 NO이다.

 

마지막으로, 탈출구에서부터 DFS나 BFS를 이용하여, 이동할 수 있는 모든 공간을 탐색한 뒤, 마지막에 미로에서 모든 G가 탈출구로 탈출할 수 있는지 확인해서 탈출할 수 있는 경우만 YES이다.

 

방법은 간단하지만, 구현 능력이 부족해서 코드가 굉장히 길고 더럽게 구현되었다..

 

다음은 정답 코드이다.

#include <bits/stdc++.h>
// 2의 제곱수 판정
#define POW2(X) (X) == ((X)&(-(X)))

// 기본설정
typedef long long ll;
using namespace std;
int dirx[4] = {1, 0, -1, 0};
int diry[4] = {0, 1, 0, -1};

// 전역변수
int n, m;	// 배열의 크기
vector<string> board(50 + 1);


// 배열 안에 위치할 수 있는지 리턴
bool inRange(int y, int x) {
	return (0 <= y && y < n && 0 <= x && x < m);
}

int main(void) {
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);

	int tc;
	cin >> tc;

	while(tc--) {
		// 초기화
		cin >> n >> m;
		int gcnt = 0, bcnt = 0;
		stack< pair<int,int> > bs;
		for(int i = 0; i < n; i++) {
			cin >> board[i];
			for(int j = 0; j < m; j++) {
				if(board[i][j] == 'G') gcnt++;
				else if(board[i][j] == 'B') {
					bs.push(make_pair(i,j));
					bcnt++;
				}
			}
		}

		if(gcnt == 0) {
			// G가 없는경우 항상 가능
			cout << "YES\n";
		} else {
			bool ret = true;
			if(0 < bcnt && ret) {
				// B가 있는 경우, B의 주변을 벽으로 막음
				while(!bs.empty()) {
					int x = bs.top().second;
					int y = bs.top().first;
					bs.pop();

					for(int i = 0; i < 4; i++) {
						int xx = x + dirx[i];
						int yy = y + diry[i];

						if(inRange(yy, xx)) {
							if(board[yy][xx] == '.') {
								board[yy][xx] = '#';
							} else if(board[yy][xx] == 'G') {
								ret = false;
							} else {
								continue;
							}
						}
					}
				}
			}
			if(!ret || board[n - 1][m - 1] == '#') {
				// B를 벽으로 막을 수 없어서 불가능
				cout << "NO\n";
			} else {
				// G가 (n - 1), (m - 1) 로 이동가능한지 확인
				bool check[50 + 1][50 + 1] = {0};
				bool canmove[50 + 1][50 + 1] = {0};
				stack< pair<int,int> > solve;
				solve.push(make_pair(n - 1, m - 1));
				while(!solve.empty()) {
					int x = solve.top().second;
					int y = solve.top().first;
					solve.pop();
					check[y][x] = true;

					if(board[y][x] == '#' || board[y][x] == 'B') {
						canmove[y][x] = false;
						continue;
					}

					for(int i = 0; i < 4; i++) {
						int xx = x + dirx[i];
						int yy = y + diry[i];

						if(inRange(yy, xx)) {
							if(!check[yy][xx] && (board[yy][xx] == '.' || board[yy][xx] == 'G')) {
								solve.push(make_pair(yy, xx));
								canmove[yy][xx] = true;
							} else{
								continue;
							}
						}
					}
				}
				for(int i = 0; i < n; i++) {
					for(int j = 0; j < m; j++) {
						if(board[i][j] == 'G') {
							ret = ret && canmove[i][j];
						}
					}
				}
				if(ret) {
					cout << "YES\n";
				} else {
					cout << "NO\n";
				}
			}
		}
	}
	return 0;
}