반응형

문제출처

https://programmers.co.kr/learn/courses/30/lessons/60058

 

코딩테스트 연습 - 괄호 변환

카카오에 신입 개발자로 입사한 콘은 선배 개발자로부터 개발역량 강화를 위해 다른 개발자가 작성한 소스 코드를 분석하여 문제점을 발견하고 수정하라는 업무 과제를 받았습니다. 소스를 컴�

programmers.co.kr

 

문제풀이

문제에서 주어진대로 구현해주면 된다. 올바른 괄호를 체크할때 스택을 사용했다. 딱히 어려운 부분은 없고 문제를 정확히 읽고 구현해주면 되는 문제인데 u를 구할때 약간 생각이 필요했다. 문자열을 탐색하면서 '('의 개수와 ')'의 개수가 같아질 때가 u이고 나머지 문자열이 v가 된다.

 

 

소스코드

https://github.com/sw93/algorithm/blob/master/Programmers/%EC%B9%B4%EC%B9%B4%EC%98%A4%20-%20%EA%B4%84%ED%98%B8%20%EB%B3%80%ED%99%98.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

 

#include <string>
#include <vector>
#include <stack>
using namespace std;

// 올바른 괄호 문자열 체크
bool isCorrectBracket(string bracket) {
    stack<char> st;
    for (int i=0; i<bracket.size(); i++) {
        if (bracket[i] == '(') {
            st.push(bracket[i]);
        } else if (bracket[i] == ')') {
            if (st.empty()) {
                return false;
            } else {
                st.pop();
            }
        }
    }
    
    if (st.empty()) return true;
    else return false;
}

// 재귀
string go(string p) {
    // 1번
    if (p == "") {
        return p;
    }
    
    // 2번
    string u = "", v = "";
    int left = 0, right = 0;
    for (int i=0; i<p.size(); i++) {
        if (p[i] == '(') left++;
        else if (p[i] == ')') right++;
        if (left == right) {
            u = p.substr(0, i + 1);
            v = p.substr(i + 1);
            break;
        }
    }
    
    // 3번
    if (isCorrectBracket(u)) {
        return u + go(v);
    } else {    // 4번
        string ret = "(" + go(v) + ")";
        u = u.substr(1, u.size() - 2);
        for (int i=0; i<u.size(); i++) {
            if (u[i] == '(') u[i] = ')';
            else if (u[i] == ')') u[i] = '(';
        }
        return ret + u;
    }
}

string solution(string p) {
    string answer = "";
    if (isCorrectBracket(p)) return p;
    answer = go(p);
    return answer;
}
반응형
반응형

문제 출처

https://programmers.co.kr/learn/courses/30/lessons/64061

 

코딩테스트 연습 - 크레인 인형뽑기 게임

[[0,0,0,0,0],[0,0,1,0,3],[0,2,5,0,1],[4,2,4,4,2],[3,5,1,3,1]] [1,5,3,5,1,2,1,4] 4

programmers.co.kr

 

문제풀이

아마 처음 긴장을 풀어주기 위해 1번 문제로 준비한 문제가 아닐까 싶다. 이 문제에서의 핵심은 스택을 사용하는 거라 생각한다. 바구니는 아래칸부터 인형이 순서대로 쌓이는 구조이며 이는 한쪽에서만 입력이 일어남을 알 수 있다. 크레인이 인형을 뽑고나서 스택의 top과 뽑은 인형이 같다면 스택을 pop하고 정답을 2 더해주면 되는 문제이다.

 

 

소스코드

https://github.com/sw93/algorithm/blob/master/Programmers/%EC%B9%B4%EC%B9%B4%EC%98%A4%20-%20%ED%81%AC%EB%A0%88%EC%9D%B8%20%EC%9D%B8%ED%98%95%EB%BD%91%EA%B8%B0%20%EA%B2%8C%EC%9E%84.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

#include <string>
#include <vector>
#include <stack>
using namespace std;

int solution(vector<vector<int>> board, vector<int> moves) {
    int answer = 0;
    stack<int> s;
    for (int i=0; i<moves.size(); i++) {
        int x = moves[i] - 1;
        
        for (int y=0; y<board.size(); y++) {
            if (board[y][x] == 0) continue;

            if (!s.empty() && (s.top() == board[y][x])) {
                s.pop();
                answer += 2;
            } else {
                s.push(board[y][x]);
            }
            
            board[y][x] = 0;
            break;
        }
    }
    return answer;
}
반응형
반응형

문제출처

https://www.acmicpc.net/problem/2178

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

 

문제풀이

최단거리를 구하는 문제이므로 BFS를 사용했다. 이 문제에서 가장 중요한 거는 input이 붙어서 주어진다는 것이다. 나 같은 경우는 map에 미로 정보를 입력하고 bfs탐색시작점을 1로 시작해 탐색한 거리마다 기존 값+1을 해주는 방식으로 풀이했다.

미로의 출구는 항상 map[n-1][m-1]의 위치이기 때문에 모든 탐색을 다 한뒤 거리를 저장한 배열의 n행 m열의 값을 출력해주면 된다. 

 

소스코드

https://github.com/sw93/algorithm/blob/master/BOJ_PS/BOJ_2178/boj2178.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

 

#include <cstdio>
#include <queue>
using namespace std;
int n, m;
const int dy[] = { -1, 0, 1, 0 };
const int dx[] = { 0, 1, 0, -1 };
int map[100][100];
int dist[100][100];
void bfs(int y, int x) {
	queue<pair<int, int> > q;
	q.push(make_pair(y, x));
	dist[y][x] = 1;

	while (!q.empty()) {
		y = q.front().first;
		x = q.front().second;
		q.pop();

		for (int i = 0; i < 4; i++) {
			int ny = y + dy[i];
			int nx = x + dx[i];
			if (ny < 0 || nx < 0 || ny >= n || nx >= m || dist[ny][nx] != 0 || map[ny][nx] != 1) continue;

			q.push(make_pair(ny, nx));
			dist[ny][nx] = dist[y][x] + 1;
		}
	}
}
int main() {
	scanf("%d %d", &n, &m);
	for (int y = 0; y < n; y++) {
		for (int x = 0; x < m; x++) {
			scanf("%1d", &map[y][x]);
		}
	}
	bfs(0, 0);
	printf("%d\n", dist[n - 1][m - 1]);
	return 0;
}
반응형
반응형

문제출처

https://www.acmicpc.net/problem/4963

 

4963번: 섬의 개수

문제 정사각형으로 이루어져 있는 섬과 바다 지도가 주어진다. 섬의 개수를 세는 프로그램을 작성하시오. 한 정사각형과 가로, 세로 또는 대각선으로 연결되어 있는 사각형은 걸어갈 수 있는 사

www.acmicpc.net

 

문제풀이

DFS, BFS 2가지 그래프 탐색 알고리즘 어떤 것을 써도 풀이 가능한 문제다. 나는 BFS를 사용했는데 입력을 받는 map배열과 방문여부를 확인하는 visit배열을 사용해서 풀이했다.

단지번호붙이기 문제와 같은 유형인데 단지 4방향 -> 8방향으로 확대되었다는 것밖에 바뀐게 없다.

 

 

소스코드

https://github.com/sw93/algorithm/blob/master/BOJ_PS/BOJ_4963/boj4963.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

 

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int w, h;
const int dy[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
const int dx[] = { 0, 1, 1, 1, 0, -1, -1, -1 };
int map[50][50];
bool visit[50][50];
void bfs(int y, int x) {
	queue<pair<int, int> > q;
	q.push(make_pair(y, x));
	visit[y][x] = 1;

	while (!q.empty()) {
		y = q.front().first;
		x = q.front().second;
		q.pop();

		for (int i = 0; i < 8; i++) {
			int ny = y + dy[i];
			int nx = x + dx[i];
			if (ny < 0 || nx < 0 || ny >= h || nx >= w || visit[ny][nx]) continue;
			if (map[ny][nx] == 1) {
				q.push(make_pair(ny, nx));
				visit[ny][nx] = 1;
			}
		}
	}
}
int main() {
	ios_base::sync_with_stdio(0); cin.tie(0);
	while (1) {
		cin >> w >> h;
		if (w == 0 && h == 0) break;
		memset(map, 0, sizeof(map));
		memset(visit, 0, sizeof(visit));
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				cin >> map[y][x];
			}
		}

		int landCount = 0;
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				if (map[y][x] == 1 && visit[y][x] == 0) {
					bfs(y, x);
					landCount++;
				}
			}
		}
		cout << landCount << "\n";
	}
	return 0;
}
반응형

'Algorithm' 카테고리의 다른 글

[프로그래머스 64061] - 카카오 기출 크레인 인형뽑기 게임  (0) 2020.05.19
[BOJ 2178] 미로 탐색  (0) 2020.05.18
[프로그래머스 64065] 튜플  (0) 2020.05.15
[BOJ 1107] 리모컨  (0) 2020.05.15
[BOJ 3085] 사탕 게임  (0) 2020.05.14
반응형

문제출처

https://programmers.co.kr/learn/courses/30/lessons/64065

 

코딩테스트 연습 - 튜플

"{{2},{2,1},{2,1,3},{2,1,3,4}}" [2, 1, 3, 4] "{{1,2,3},{2,1},{1,2,4,3},{2}}" [2, 1, 3, 4] "{{4,2,3},{3},{2,3,4,1},{2,3}}" [3, 2, 4, 1]

programmers.co.kr

 

문제풀이

1. ",{" 기준으로 문자열을 잘라 집합을 구한 후 집합의 크기를 기준으로 정렬한다.
2.  i번째 집합에 속해있는 숫자묶음을 추출한뒤 answer 리스트에 없는 숫자만 추가해주면 된다.


c++로 풀면 꽤나 소스도 길어지고 빡빡했을거 같았다. 왜냐하면 숫자가 아닌 문자들을 다 제외하는 로직을 구현을 시작해야 해서 이참에 파이썬을 한번 써봤다.
왜 문자열은 파이썬을 사용하면 금방 풀 수 있다고 하는지 느꼈다.
문자열 문제에 항상 힘들어했는데 파이썬을 애용해봐야겠다

 

 

소스코드

https://github.com/sw93/algorithm/blob/master/Programmers/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%20-%20%ED%8A%9C%ED%94%8C.py

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

 

import re

def solution(s):
    answer = [] # 정답
    numberSet = s.split(',{') # 집합을 구함
    numberSet.sort(key = len) # 집합 크기를 기준으로 정렬

    for i in numberSet :
    	number = re.findall("\d+", i) # 숫자묶음을 추출
    	for j in number :
    		if int(j) not in answer :
    			answer.append(int(j))
    return answer
반응형

'Algorithm' 카테고리의 다른 글

[BOJ 2178] 미로 탐색  (0) 2020.05.18
[BOJ 4963] 섬의 개수  (0) 2020.05.18
[BOJ 1107] 리모컨  (0) 2020.05.15
[BOJ 3085] 사탕 게임  (0) 2020.05.14
[BOJ 17143] 낚시왕  (0) 2020.05.13
반응형

문제출처

https://www.acmicpc.net/problem/1107

 

1107번: 리모컨

첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 ≤ N ≤ 500,000)이 주어진다.  둘째 줄에는 고장난 버튼의 개수 M (0 ≤ M ≤ 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼��

www.acmicpc.net

 

문제풀이

완전탐색 문제인데 약간 생각할 부분이 있었다. n의 범위가 50만까지인데 채널은 무한이기 때문에 뒤에서 탐색하는 경우 버튼을 더 적게 누를수도 있기 때문이다. 또 숫자버튼을 누르고 + 또는 -버튼 1개만 계속 눌러야 한다. 왜냐하면 +버튼을 누르다 -버튼을 누르고 다시 +버튼을 누르면 중복이 발생하기 때문에 최소값이 될 수 없기 때문이다.

이후 단순 구현만 해주면 된다.

 

소스코드

https://github.com/sw93/algorithm/blob/master/BOJ_PS/BOJ_1107/boj1107.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

 

#include <iostream>
#include <algorithm>
using namespace std;
int n, m;
bool brokenButton[10];

// 숫자버튼을 누른 채널으로 이동 가능한지 확인하는 함수
bool checkButton(int channel) {
	if (channel == 0) {
		if (brokenButton[0]) return false;
		else return true;
	}
	while (channel) {
		if (brokenButton[channel % 10]) return false;
		else channel /= 10;
	}
	return true;
}

// checkButton을 통과한 채널의 자릿수를 구하는 함수
int getChannelDigit(int channel) {
	int ret = 0;
	if (channel == 0) return 1;
	while (channel) {
		channel /= 10;
		ret++;
	}
	return ret;
}

int main() {
	ios_base::sync_with_stdio(0); cin.tie(0);
	cin >> n >> m;
	int buttonNumber;
	for (int i = 0; i < m; i++) {
		cin >> buttonNumber;
		brokenButton[buttonNumber] = 1;
	}

	int ans = 987654321;

	// 뒤에서 탐색하는게 더 효과적인 경우도 있으므로 n의 범위를 2배 늘림
	// 예를 들어 6버튼만 안고장나고 60만번 채널로 이동하고 싶은 경우
	// 초기 100번 채널에서 +버튼만 누르는 것 보다 666,666채널에서 -버튼을 누르는 횟수가 더 적음
	for (int i = 0; i <= 1000000; i++) {
		int channel = i;
		if (checkButton(channel)) {

			// 숫자버튼을 눌러 이동할때 필요한 자릿수
			int digit = getChannelDigit(channel);

			// 숫자버튼 이동과 +버튼 또는 -버튼 클릭의 합을 비교
			ans = min(ans, digit + abs(n - channel));
		}
	}

	cout << min(ans, abs(n - 100));
	return 0;
}
반응형

'Algorithm' 카테고리의 다른 글

[BOJ 4963] 섬의 개수  (0) 2020.05.18
[프로그래머스 64065] 튜플  (0) 2020.05.15
[BOJ 3085] 사탕 게임  (0) 2020.05.14
[BOJ 17143] 낚시왕  (0) 2020.05.13
[BOJ 17779] 게리맨더링2  (0) 2020.05.12
반응형

문제 출처

https://www.acmicpc.net/problem/3085

 

3085번: 사탕 게임

문제 상근이는 어렸을 적에 "봄보니 (Bomboni)" 게임을 즐겨했다. 가장 처음에 N×N크기에 사탕을 채워 놓는다. 사탕의 색은 모두 같지 않을 수도 있다. 상근이는 사탕의 색이 다른 인접한 두 칸을 고

www.acmicpc.net

 

문제 풀이

모든 경우의 수를 다 탐색하는 문제다. 위치마다 4방향 탐색을 해도 되지만 위쪽과 왼쪽은 조금만 생각해보면 중복되는 처리임을 알 수 있다.

시간 복잡도는 사탕의 위치를 바꾸는 데 2방향(오른쪽, 아래)과 n의 크기로 구하는데 2 X 50 X 50 , 최대 개수를 구하는데는 50 X 50이다. 따라서 2 X 50^4 = 12,500,000으로 1초 안에 풀이가 가능하다.

즉 O(N^4)의 시간 복잡도를 가진 이 알고리즘은 N이 50이하로 작게 제한되어 있기 때문에 완전 탐색을 해도 충분히 풀이할 수 있다고 판단했다.

 

소스 코드

https://github.com/sw93/algorithm/blob/master/BOJ_PS/BOJ_3085/boj3085.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

 

#include <iostream>
#include <algorithm>
using namespace std;
int n;
char map[50][50];
void init() {
	cin >> n;
	for (int y = 0; y < n; y++) {
		for (int x = 0; x < n; x++) {
			cin >> map[y][x];
		}
	}
}
int eatCandy() {
	int ret = 1;

	// 열 체크
	for (int y = 0; y < n; y++) {
		int count = 1;
		for (int x = 1; x < n; x++) {
			if (map[y][x] == map[y][x - 1]) count++;
			else count = 1;
			ret = max(count, ret);
		}
	}

	// 행 체크
	for (int x = 0; x < n; x++) {
		int count = 1;
		for (int y = 1; y < n; y++) {
			if (map[y][x] == map[y - 1][x]) count++;
			else count = 1;
			ret = max(count, ret);
		}
	}

	return ret;
}
int main() {
	ios_base::sync_with_stdio(0); cin.tie(0);
	init();
	int ans = 0;
	for (int y = 0; y < n; y++) {
		for (int x = 0; x < n; x++) {

			// 오른쪽 탐색
			if (x + 1 < n) {
				swap(map[y][x], map[y][x + 1]);
				ans = max(ans, eatCandy());
				swap(map[y][x], map[y][x + 1]);
			}

			// 아래쪽 탐색
			if (y + 1 < n) {
				swap(map[y][x], map[y + 1][x]);
				ans = max(ans, eatCandy());
				swap(map[y][x], map[y + 1][x]);
			}
		}
	}
	cout << ans;
	return 0;
}
반응형

'Algorithm' 카테고리의 다른 글

[프로그래머스 64065] 튜플  (0) 2020.05.15
[BOJ 1107] 리모컨  (0) 2020.05.15
[BOJ 17143] 낚시왕  (0) 2020.05.13
[BOJ 17779] 게리맨더링2  (0) 2020.05.12
[프로그래머스 17678 - 카카오 2018 1차] 셔틀 버스  (0) 2020.05.06
반응형

문제출처

https://www.acmicpc.net/problem/17143

 

17143번: 낚시왕

낚시왕이 상어 낚시를 하는 곳은 크기가 R×C인 격자판으로 나타낼 수 있다. 격자판의 각 칸은 (r, c)로 나타낼 수 있다. r은 행, c는 열이고, (R, C)는 아래 그림에서 가장 오른쪽 아래에 있는 칸이다.

www.acmicpc.net

 

문제풀이

 

처음에 시간복잡도를 생각 안하고 구현했더니 시간초과가 났다. 다음부터는 문제풀이 이전에 꼭 시간복잡도를 먼저 계산하고 풀이해야겠다.

 

이 문제는 상어의 정보가 주어지고 낚시왕이 왼쪽에서 오른쪽으로 이동하면서 잡은 상어의 크기의 총 합을 출력하는 문제이다.

문제에서 요구하는 구현사항은 2가지인데 순서에 맞게 구현해줘야 한다.

 

나 같은 경우는 낚시왕이 (0, 0)에서 시작해 x축 이동을 하면서 1번을 구현했다.

 

2번 조건을 구현하기 위해 catchShark(int x) 함수를 만들었다.

catchShark(int x) 함수

낚시왕이 위치한 x좌표를 넘겨줘서 y축 탐색을 해 가장 가까운 상어를 잡는 함수이다.

int catchShark(int x) {
	int ret = 0;
	for (int y = 1; y <= r; y++) {
		if (map[y][x] != 0) {
			ret = v[map[y][x]].z;
			v[map[y][x]].dead = 1;
			map[y][x] = 0;

			break;
		}
	}
	return ret;
}

 

3번 조건이 가장 까다로웠다. 이 조건은 moveShark() 함수를 만들었다.

moveShark() 함수

살아있는 상어 수만큼 반복하면서 상어 좌표를 이동해주는 함수이다. 행 처리와 열 처리가 같기 때문에 열 처리를 기준으로 설명하겠습니다.

여기서 가장 중요한게 speed = speed % ((c - 1) * 2) 부분인데 이 처리를 하지 않으면 시간초과가 납니다.

만약 r=4, c=6인 경우 상어가 방향전환을 하고 움직일때 1번 열과 6번 열은 1번씩만 자리잡고 2, 3, 4, 5번 열은 2번씩 자리잡는다. 

 

빨간 색을 1번만 위치하는 열이고 파랑색은 기존 위치 초록색을 방향전환을 했을때 자리잡는 위치이다. 이 표를 가지고 시뮬레이션을 해보면 규칙을 파악할 수 있다. 이 규칙을 이용해서 반복횟수를 줄여 시간초과를 벗어나면 된다.

 

이후 조건에 따라 구현해줬다. 상어가 이동한 자리가 빈칸이면 상어 번호를 map에 저장하고 움직인 상어가 위치한다면 크기를 비교해 먹거나 먹히도록 구현했다. 움직이지 않은 상어가 위치하는 경우는 반복문을 통해 나중에 처리하기 때문에 현재 위치한 상어를 위치시키면 된다.

 

void moveShark() {
	for (int i = 1; i <= m; i++) {

		// 죽은 상어는 건너뛴다
		if (v[i].dead) continue;

		// 움직일 상어 정보
		int y = v[i].r;
		int x = v[i].c;
		int speed = v[i].s;
		int dir = v[i].d;
		int size = v[i].z;

		// 현재 움직일 상어 기존 위치 초기화
		if (map[y][x] == i) map[y][x] = 0;

		// 위, 아래
		if (dir == 0 || dir == 1) {
			speed = speed % ((r - 1) * 2);
			
			while (speed--) {
				int ny = y + dy[dir];
				if (1 > ny || ny > r) dir = 1 - dir;
				y += dy[dir];
			}
		} 
		
		// 오른쪽 왼쪽
		else { 
			speed = speed % ((c - 1) * 2);

			while (speed--) {
				int nx = x + dx[dir];
				if (1 > nx || nx > c) dir = 5 - dir;
				x += dx[dir];
			}
		}

		// 이동한 상어 위치 최신화
		v[i].r = y;
		v[i].c = x;
		v[i].d = dir;

		// 이동한 위치가 빈칸인 경우 => 위치 이동
		if (map[y][x] == 0) {
			map[y][x] = i;
		} 

		// 이동한 위치에 이미 이동을 완료한 상어가 있는 경우 => 잡아먹거나 잡아 먹힘
		else if (i > map[y][x]) { 

			// 이동한 상어의 사이즈가 더 큰 경우
			if (size > v[map[y][x]].z) {
				v[map[y][x]].dead = 1;
				map[y][x] = i;
			}

			// 이동한 상어의 사이즈가 더 작은 경우 
			else {
				v[i].dead = 1;
			}
		} 
		
		// 이동한 위치에 아직 이동 안 한 상어가 있는 경우 => 이후에 재 처리하기 때문에 뒤집어 씌움
		else { 
			map[y][x] = i;
		}
	}
}

 

기존에 시간초과했던 방법 즉, 문제대로 그냥 구현한 방법의 시간복잡도는 다음과 같다.

상어의수(10000) X 속도(1000) X 낚시왕이 움직이는 열의 수(100) 

= 10^4 * 10^3 * 10^2 = 10^9 = 1,000,000,000 (10억) 번의 연산이 필요하다.

1초에 1억번 계산가능하다는 가정하에 이 문제는 단순 구현으로는 1초안에 해결할 수 없다.

 

시간 초과때문에 힘들었던 문제인데 진짜 시간복잡도를 먼저 계산하는게 왜 중요한지 다시 깨우친 문제이다. 시험장에서였으면 아마 시간초과로 탈락했을거 같다.

 

소스코드

https://github.com/sw93/algorithm/blob/master/SW_TEST(samsung)/BOJ_17143/boj17143.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

 

#include <iostream>
#include <vector>
using namespace std;
struct SHARK {
	int r, c, s, d, z;
	bool dead;
};
int r, c, m;
int map[101][101];
const int dy[] = { -1, 1, 0, 0 };
const int dx[] = { 0, 0, 1, -1 };
vector<SHARK> v;

void init() {
	cin >> r >> c >> m;

	// 1번부터 시작
	v.push_back({ 0,0,0,0,0,0 });
	for (int i = 1; i <= m; i++) {
		SHARK shark;
		cin >> shark.r >> shark.c >> shark.s >> shark.d >> shark.z;
		shark.dead = 0;
		shark.d -= 1;
		v.push_back(shark);
		map[shark.r][shark.c] = i;
	}
}

int catchShark(int x) {
	int ret = 0;
	for (int y = 1; y <= r; y++) {
		if (map[y][x] != 0) {
			ret = v[map[y][x]].z;
			v[map[y][x]].dead = 1;
			map[y][x] = 0;

			break;
		}
	}
	return ret;
}

void moveShark() {
	for (int i = 1; i <= m; i++) {

		// 죽은 상어는 건너뛴다
		if (v[i].dead) continue;

		// 움직일 상어 정보
		int y = v[i].r;
		int x = v[i].c;
		int speed = v[i].s;
		int dir = v[i].d;
		int size = v[i].z;

		// 현재 움직일 상어 기존 위치 초기화
		if (map[y][x] == i) map[y][x] = 0;

		// 위, 아래
		if (dir == 0 || dir == 1) {
			speed = speed % ((r - 1) * 2);
			
			while (speed--) {
				int ny = y + dy[dir];
				if (1 > ny || ny > r) dir = 1 - dir;
				y += dy[dir];
			}
		} 
		
		// 오른쪽 왼쪽
		else { 
			speed = speed % ((c - 1) * 2);

			while (speed--) {
				int nx = x + dx[dir];
				if (1 > nx || nx > c) dir = 5 - dir;
				x += dx[dir];
			}
		}

		// 이동한 상어 위치 최신화
		v[i].r = y;
		v[i].c = x;
		v[i].d = dir;

		// 이동한 위치가 빈칸인 경우 => 위치 이동
		if (map[y][x] == 0) {
			map[y][x] = i;
		} 

		// 이동한 위치에 이미 이동을 완료한 상어가 있는 경우 => 잡아먹거나 잡아 먹힘
		else if (i > map[y][x]) { 

			// 이동한 상어의 사이즈가 더 큰 경우
			if (size > v[map[y][x]].z) {
				v[map[y][x]].dead = 1;
				map[y][x] = i;
			}

			// 이동한 상어의 사이즈가 더 작은 경우 
			else {
				v[i].dead = 1;
			}
		} 
		
		// 이동한 위치에 아직 이동 안 한 상어가 있는 경우 => 이후에 재 처리하기 때문에 뒤집어 씌움
		else { 
			map[y][x] = i;
		}
	}
}
int main() {
	ios_base::sync_with_stdio(0); cin.tie(0);
	init();

	// 예외 처리
	if (m == 0) {
		cout << 0;
		return 0;
	}

	int ans = 0;
	for (int x = 1; x <= c; x++) {
		ans += catchShark(x);
		moveShark();
	}
	cout << ans;
	return 0;
}
반응형
반응형

문제출처

https://www.acmicpc.net/problem/17779

 

17779번: 게리맨더링 2

재현시의 시장 구재현은 지난 몇 년간 게리맨더링을 통해서 자신의 당에게 유리하게 선거구를 획정했다. 견제할 권력이 없어진 구재현은 권력을 매우 부당하게 행사했고, 심지어는 시의 이름��

www.acmicpc.net

 

문제풀이

문제를 이해하는데 너무 오래걸렸다. 이해력이 부족한건가..

n X n 배열에서 5개의 선거구를 만들어 선거구의 인구의 최댓값과 최솟값의 차이가 가장 적은 결과를 출력하는 문제이다.

 

이 문제는 가장 중요한게 1, 2, 3, 4번 지구의 경계선을 어떻게 나누는지 이해하는게 가장 중요하다.

처음에 d1과 d2의 값을 어떻게 해야 하는지 고민이 많았는데 문제를 자세히 읽고 1, 2, 3, 4번 꼭지점의 좌표를 적어보면 이해하기 편하다.

 

 

 

1번 : (y, x)

2번 : (y+d2, x+d2)

3번 : (y+d1, x-d1)

4번 : (y+d1+d2, x-d1+d2)

즉, d1은 왼쪽대각선으로 이동하는 칸의 수이고 d2는 오른쪽대각선으로 이동하는 칸의 수임을 유추할 수 있다.

그러면 d1의 범위를 알 수 있다. d1은 3번꼭지점의 x좌표를 기준으로 생각해 보자.

3번 꼭지점의 x-d1은 0이거나 0보다 커야한다. (x-d1 >= 0) 이 식을 d1으로 정리하면 d1<= x의 식이 나온다.

마찬가지로 d2의 범위는 2번 꼭지점의 x좌표를 기준으로 생각해보자. 

x+d2는 마찬가지고 n X n 배열을 벋어날수 없기 때문에 x+d2 <= n의 식이 나오고 정리하면 d2 <= n-x 이다.

 

위와 같이 d1과 d2의 범위를 구하고 전체탐색을 한다. 이때 y, x, d1, d2의 값으로 경계선을 만들 수 없는 경우는 제외한다. 경계선을 만들 수 있으면 꼭지점 정보를 vector에 저장하고 1번 지역구부터 4번 지역구까지 tempMap에 값을 넣어줬다.

 

1번 선거구의 경우 0행부터 3번꼭지점 행-1까지 범위안에 들어와야한다.  열은 0열부터 1번꼭지점까지의 열이다.

이때 그림을 보면 y값(행)이 증가할때 마다 열값이 1개씩 감소하는 것을 알 수 있다. 때문에 y값(행)이 1번꼭지점의 y값보다 크거나 같으면 갯수를 세어 열을 채워줄때 빼줬다. 이후 2, 3, 4번 지역구도 동일하게 진행.

이 부분은 코드를 보는게 더 이해가 빠를듯 하다.

 

이후 n X n 배열을 순회하면서 1~5번의 값을 기준으로 map에 있는 인구수를 벡터에 저장해 오름차순으로 정렬한뒤 차이를 구했다. 이 차이는 경계선이 정상적으로 그려질때의 값이므로 값을 구할때마다 min함수를 이용해 차이를 최신화해줬다.

 

문제를 이해하는게 정말 어려웠다. 처음에 y, x, d1, d2의 부등식을 정리하려고 애썼는데 사실 지금도 어떻게 정리하는지 모르겠다... (혹시 x<x+d1+d2<=n, 1<=y-d1<y<y+d2<=n) 이 식을 정리해서 문제풀이 하신분 댓글 부탁드립니다.

 

삼성기출 문제가 최신문제일수록 좀더 난이도가 생기는 느낌이다. 뭔가 전체탐색을 하면서 시뮬레이션이 섞여있거나 시뮬레이션 조건이 까다로워지는건 나만의 느낌인가....?

 

소스코드

https://github.com/sw93/algorithm/blob/master/SW_TEST(samsung)/BOJ_17779/boj17779.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

 

#include <iostream>
#include <vector>
#include <algorithm>
#define INF 987654321
using namespace std;
int n, ans = INF;
int map[20][20];
int tempMap[20][20];
vector<pair<int, int> > v;
void init() {
	cin >> n;
	for (int y = 0; y < n; y++) {
		for (int x = 0; x < n; x++) {
			cin >> map[y][x];
		}
	}
}
bool checkBoard(int y, int x, int d1, int d2) {

	// 3번 꼭지점
	if (y + d1 >= n || x - d1 < 0) return false;

	// 2번 꼭지점
	if (y + d2 >= n || x + d2 >= n) return false;

	// 4번 꼭지점
	if (y + d1 + d2 >= n || x - d1 + d2 < 0) return false;
	return true;
}
void calc() {
	vector<int> ret(5, 0);
	for (int y = 0; y < n; y++) {
		for (int x = 0; x < n; x++) {
			if (tempMap[y][x] == 1) ret[0] += map[y][x];
			else if (tempMap[y][x] == 2) ret[1] += map[y][x];
			else if (tempMap[y][x] == 3) ret[2] += map[y][x];
			else if (tempMap[y][x] == 4) ret[3] += map[y][x];
			else if (tempMap[y][x] == 5) ret[4] += map[y][x];
		}
	}

	sort(ret.begin(), ret.end());
	int diff = ret[4] - ret[0];
	ans = min(ans, diff);
}
void solve() {

	// 5번 지역구로 모두 초기화 (1, 2, 3, 4번 지역구가 아니면 5번 지역구)
	for (int y = 0; y < n; y++) {
		for (int x = 0; x < n; x++) {
			tempMap[y][x] = 5;
		}
	}

	// 1번 지역구
	int minusCount = 0;
	for (int y = 0; y < v[1].first; y++) {
		if (y >= v[0].first) minusCount++;
		for (int x = 0; x <= v[0].second - minusCount; x++) {
			tempMap[y][x] = 1;
		}
	}

	// 2번 지역구
	minusCount = 0;
	for (int y = 0; y <= v[2].first; y++) {
		if (y > v[0].first) minusCount++;
		for (int x = v[0].second + minusCount + 1; x < n; x++) {
			tempMap[y][x] = 2;
		}
	}

	// 3번 지역구
	minusCount = 0;
	for (int y = n - 1; y >= v[1].first; y--) {
		if (y < v[3].first) minusCount++;
		for (int x = 0; x < v[3].second - minusCount; x++) {
			tempMap[y][x] = 3;
		}
	}

	// 4번 지역구
	minusCount = 0;
	for (int y = n - 1; y > v[2].first; y--) {
		if (y <= v[3].first) minusCount++;
		for (int x = v[3].second + minusCount; x < n; x++) {
			tempMap[y][x] = 4;
		}
	}

	calc();
	v.clear();
}
int main() {
	ios_base::sync_with_stdio(0); cin.tie(0);
	init();
	for (int y = 0; y < n; y++) {
		for (int x = 0; x < n; x++) {

			// d1 = 왼쪽 대각선 이동
			// 3번 꼭지점 = (y+d1, x-d1)
			// x-d1값이 0보다 크거나 같아야 함 (x - d1 >= 0)
			// 따라서 d1 <= x
			for (int d1 = 1; d1 <= x; d1++) {

				// d2 = 오른쪽 대각선 이동
				// 2번 꼭지점 = (y+d2, x+d2)
				// x+d2값이 n보다 작거나 같아야 함 (x + d2 <= n)
				// 따라서 d2 <= n-x
				for (int d2 = 1; d2 <= n - x; d2++) {
					if (checkBoard(y, x, d1, d2)) {
						// 선거구 경계 위치
						v.push_back(make_pair(y, x));
						v.push_back(make_pair(y + d1, x - d1));
						v.push_back(make_pair(y + d2, x + d2));
						v.push_back(make_pair(y + d1 + d2, x - d1 + d2));
						solve();
					}
				}
			}
		}
	}
	cout << ans;
	return 0;
}
반응형
반응형

문제출처

https://programmers.co.kr/learn/courses/30/lessons/17678

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

문제풀이

string 형태의 시간을 int형 분으로 변환하여 풀이했다. 변환한 벡터를 오름차순으로 정렬한 뒤 순차적으로 크루들을 탑승시킨다. 이때 마지막 버스인데 타지 못할 경우 마지막 탑승자보다 1분 빨리오는 예외처리가 필요하다. 

2번째 for문에서 탑승할 인원을 체크하고 예외 처리 이후 int형 분을 다시 string형 시간으로 변환하여 return해주면 되는 문제.

핵심은 주어진 문자열을 연산할 때 어떻게 처리하고 정렬을 이용하는 것 같다.

 

소스코드

https://github.com/sw93/algorithm/blob/master/Programmers/kakao2018%20-%20%EC%85%94%ED%8B%80%EB%B2%84%EC%8A%A4.cpp

 

sw93/algorithm

problem solve. Contribute to sw93/algorithm development by creating an account on GitHub.

github.com

#include <string>
#include <vector>
#include <algorithm>
using namespace std;
const int START_BUS_MINUTE = 9 * 60;
const int LAST_BUS_MINUTE = 24 * 60;
int getIntMinute(string time) {
    int ret = ((time[0]-'0')*10 + (time[1]-'0')) * 60;
    ret += (time[3]-'0')*10 + (time[4]-'0');
    return ret;
}
string getStringTime(int time) {
    string ret = to_string((time/600));
    ret += to_string((time/60)%10);
    ret +=  ':';
    ret += to_string((time%60)/10);
    ret += to_string((time%60)%10);
    return ret;
}
string solution(int n, int t, int m, vector<string> timetable) {
    string ret = "";
    vector<int> v;
    for (int i=0; i<timetable.size(); i++) v.push_back(getIntMinute(timetable[i]));
    sort(v.begin(), v.end());
    
    int boardCount = 0;
    int startTime = START_BUS_MINUTE;
    for (int i=0; i<n; i++) {

        // 문제 조건 23:59까지만 가능
    	if (startTime > LAST_BUS_MINUTE) break;
    	int maxBoardCount = m;
        
    	for (int j=boardCount; j<v.size(); j++) {
    		if (maxBoardCount == 0 || v[j] > startTime) break;
            boardCount++;
            maxBoardCount--;
    	}

        // 마지막 버스
    	if (i == n-1) {
            
          // 탑승할 자리가 없는 경우 1분 일찍 나옴
          if (maxBoardCount == 0) startTime = v[boardCount-1] - 1;  
    	}  else startTime += t;
    }
    ret = getStringTime(startTime);
    return ret;
}
반응형

+ Recent posts