-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
필수 16236, 선택 10775, 1976, 20040 추가제출 합니다.
- Loading branch information
1 parent
372d3c8
commit 4572635
Showing
4 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// | ||
// Created by user on 2021-11-30. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
|
||
using namespace std; | ||
|
||
vector<int> parent; | ||
|
||
//Find 연산 | ||
int findParent(int node) { | ||
if (parent[node] == -1) //루트 정점 | ||
return node; // 입력한 노드가 루트 정점이므로 노드 반환 | ||
return parent[node] = findParent(parent[node]); //그래프 압축하며 루트 정점 찾기 | ||
} | ||
|
||
//Union 연산 | ||
bool unionGate(int gate) { | ||
int possible_gate = findParent(gate); //도킹 가능한 게이트 | ||
if (possible_gate == 0) //도킹 가능한 게이트가 없으므로 | ||
return false; // 도킹 불가 | ||
parent[possible_gate] = possible_gate - 1; //도킹 처리 | ||
return true; // 도킹 가능 | ||
} | ||
|
||
/** | ||
* 그리디 | ||
* 1. 1 ~ gi 까지의 게이트 중 어떤 게이트에 도킹해야 할까? | ||
* 2. 1번 게이트부터 도킹한다면... g1 = 3, g2 = 1인 경우 1대의 비행기만 도킹할 수 있음 | ||
* 3. gi번 게이트부터 도킹한다면... g1 = 3, g2 = 1인 경우 2대의 비행기 모두 도킹할 수 있음 | ||
* -> gi번 게이트부터 1번 게이트까지 가능한 게이트를 찾으면 바로 도킹 (그리디) | ||
* | ||
* 유니온 파인드 | ||
* 1. 만약 1번 게이트가 비어있고, 2 ~ 1,000번 게이트가 도킹되어 있으며 gi = 1,000인 입력이 들어왔을 때... | ||
* 2. 1,000번 게이트부터 하나하나 거슬러 올라가며 1번 게이트까지 가는 것은 비효율적 (시간초과) | ||
* 3. 도킹할 때마다 이번에 도킹한 게이트의 바로 이전 게이트를 집합의 루트 정점으로 만들어 바로 접근할 수 있도록 설정 | ||
* | ||
* 비슷한 문제 : https://tech.kakao.com/2020/04/01/2019-internship-test/ (문제 4) | ||
* 문제 해설 도움 받은 링크: https://steady-coding.tistory.com/114 | ||
*/ | ||
int main() { | ||
int g, p, gi; // g: 게이트의 수, p: 비행기의 수, gi: 맨처음 도킹 시도할 게이트 번호 | ||
|
||
//입력 | ||
cin >> g >> p; | ||
parent.assign(g + 1, -1); // 게이트의 수만큼 정점 노드 초기화 | ||
for (int i = 0; i < p; i++) { // p개의 줄에 | ||
cin >> gi; // gi가 주어진다. | ||
|
||
//연산 | ||
if (!unionGate(gi)) { //도킹 불가능 | ||
cout << i; // 몇 개까지 도킹했는지 출력 | ||
return 0; // 프로그램 종료 | ||
} | ||
} | ||
|
||
//출력 | ||
cout << p; // 비행기 모두 모두 도킹 가능하다면(종료되지 않고 반복문 탈출했다면) p 출력 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// | ||
// Created by user on 2021-11-30. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
#include <queue> | ||
#include <algorithm> | ||
|
||
using namespace std; | ||
const int INF = 401; // 공간의 최대 | ||
typedef pair<int, int> ci; // 상어의 위치 | ||
|
||
pair<int, ci> nextPos(int n, int shark_size, ci &shark, vector<vector<int>> &board) { | ||
int dr[4] = {-1, 1, 0, 0}; | ||
int dc[4] = {0, 0, -1, 1}; | ||
// 순서대로 상, 하, 좌, 우 | ||
|
||
int min_dist = INF; // 최소 값의 초기값은 최대로 설정 | ||
queue<ci> q; //상어가 갈 수 있는 곳 | ||
vector<vector<int>> visited(n, vector<int>(n, 0)); //상어의 방문 여부 | ||
vector<ci> list; //상어가 먹을 수 있는 물고기들의 위치 | ||
|
||
visited[shark.first][shark.second] = 1; | ||
q.push(shark); // 방문했다고 표시하고 상어가 갈 수 있는 곳으로 추가 | ||
while (!q.empty()) { // 상어가 갈 수 있는 곳이 존재하는 동안 | ||
int row = q.front().first; // 행 설정 | ||
int col = q.front().second; // 열 | ||
int dist = visited[row][col]; // 거리 | ||
q.pop(); // 큐 상단에 있는 값 pop | ||
|
||
if (dist >= min_dist) //최단거리 이상은 탐색할 필요 없음 | ||
continue; | ||
for (int i = 0; i < 4; i++) { | ||
int nr = row + dr[i]; // 위치 바꿔가면서 | ||
int nc = col + dc[i]; | ||
if (nr < 0 || nr >= n || nc < 0 || nc >= n || visited[nr][nc] || board[nr][nc] > shark_size) | ||
// 이동 불가하다면(보드의 범위 밖이거나 아무것도 없다면) | ||
continue; // 탐색할 필요 없다 | ||
|
||
visited[nr][nc] = visited[row][col] + 1; // 범위 내라면 상어의 위치 + 1 | ||
if (board[nr][nc] && board[nr][nc] < shark_size) { //먹을 수 있는 물고기 발견 | ||
list.emplace_back(nr, nc); // 먹을 수 있는 물고기들의 위치에 추가 | ||
min_dist = visited[nr][nc]; // 최소 거리 갱신 | ||
continue; | ||
} | ||
q.push({nr, nc}); // 먹을 수 있는 물고기를 찾았다면 더 나갈 필요 없다 | ||
} | ||
} | ||
|
||
if (list.empty()) //상어가 갈 수 있는 곳이 없음 | ||
return {min_dist, {-1, -1}}; // | ||
|
||
sort(list.begin(), list.end(), [](const ci &p1, const ci &p2) { //정렬 | ||
if (p1.first != p2.first) // 조건에 맞는 물고기의 좌표가 맨 앞으로 올 수 있게 정렬 | ||
return p1.first < p2.first; // 위로 먼저 이동 | ||
return p1.second < p2.second; // 왼쪽으로 먼저 이동 | ||
}); | ||
return {min_dist - 1, list[0]}; // 조건에 맞는 물고기의 좌표 넘겨줌 | ||
} | ||
|
||
int simulation(int n, pair<int, int> &shark, vector<vector<int>> &board) { | ||
int ans = 0, size = 2, cnt = 0; // size: 상어의 크기 (초기값 2로 설정), cnt: 먹은 물고기의 수 | ||
while (true) { | ||
pair<int, ci> result = nextPos(n, size, shark, board); // 위치 get | ||
if (result.first == INF) //더 이상 먹을 수 있는 물고기가 공간에 없음 | ||
break; // 반복문 종료 | ||
ans += result.first; // 먹을 수 있다면 정답에 추가 | ||
cnt++; // 먹은 물고기 수 증가 | ||
if (cnt == size) { //상어 크기 증가 | ||
cnt = 0; // 계산했으니 cnt 0으로 초기화 | ||
size++; // 크기 증가 | ||
} | ||
|
||
//상어 이동 | ||
ci pos = result.second; | ||
board[shark.first][shark.second] = 0; // 그 위치에 있는 물고기를 먹었읜 그 위치는 이제 빈 칸 | ||
shark = pos; // 상어 위치 재설정 | ||
} | ||
return ans; | ||
} | ||
|
||
/** | ||
* 1. 상어로부터 가장 가까운 거리에 있는 모든 물고기 탐색 (BFS) | ||
* 2. 우선순위 조건에 맞추어 먹으러 갈 물고기 확정 | ||
* 탐색하는 방향에 우선순위를 두는 걸로 해결되지 않음! (예제 입력 4) 정렬 필요 | ||
* 3. 상어가 이동할 수 있는 곳이 없을 때까지 BFS 탐색 반복 | ||
* | ||
* 입력 범위가 작기 때문에 매번 BFS 탐색을 반복해도 시간 초과 X | ||
* 가능한 물고기의 최대 마리 수 : 399마리 | ||
* 최대 BFS 탐색 횟수 : 399회, 1회 탐색마다 while 문은 최대 400회 미만으로 순회 | ||
* 총 연산 횟수 약 160000번으로 충분히 가능 | ||
* | ||
* 해설 : https://myunji.tistory.com/378 | ||
* *글 자체는 별로 도움이 안되고...그냥 리팩토링하면 코드가 이렇게 되는구나 정도만 봐주세요 | ||
*/ | ||
int main() { | ||
int n; // 공간의 크기: n X n | ||
pair<int, int> shark_pos; // 상어의 위치 | ||
|
||
//입력 | ||
cin >> n; | ||
vector<vector<int>> board(n, vector<int>(n)); // n X n만큼의 공간 생성 | ||
for (int i = 0; i < n; i++) { | ||
for (int j = 0; j < n; j++) { | ||
cin >> board[i][j]; // 각 칸의 상태 입력받기 | ||
if (board[i][j] == 9) //상어의 초기 위치 | ||
shark_pos = make_pair(i, j); // 초기 위치를 벡터값으로 저장 | ||
} | ||
} | ||
|
||
//연산 & 출력 | ||
cout << simulation(n, shark_pos, board); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// | ||
// Created by user on 2021-11-30. | ||
// | ||
#include <iostream> | ||
#include <vector> | ||
|
||
using namespace std; | ||
|
||
vector<int> parent; | ||
|
||
//Find 연산 | ||
int findParent(int node) { | ||
if (parent[node] < 0) //루트 정점 | ||
return node; // 입력한 노드가 루트 정점이므로 노드 반환 | ||
return parent[node] = findParent(parent[node]); //그래프 압축하며 루트 정점 찾기 | ||
} | ||
|
||
//Union 연산 | ||
void unionInput(int x, int y) { | ||
int xp = findParent(x); // x의 루트 정점 | ||
int yp = findParent(y); // y의 루트 정점 | ||
|
||
if (xp == yp) //같은 집합에 있다면 유니온 할 수 없음 | ||
return; // 할 수 없으므로 함수 종료 | ||
if (parent[xp] < parent[yp]) { //새로운 루트 xp | ||
parent[xp] += parent[yp]; // xp에 yp 트리 추가 | ||
parent[yp] = xp; // yp의 루트 정점을 xp로 설정 | ||
} else { //새로운 루트 yp | ||
parent[yp] += parent[xp]; // yp에 xp 추가 | ||
parent[xp] = yp; // xp의 루트 정점을 yp로 설정 | ||
} | ||
} | ||
|
||
/** | ||
* 입력으로 주어지는 i, j 도시의 연결정보를 통해 서로소 집합을 만든 후, | ||
* 여행 계획으로 세운 도시들이 모두 같은 집합에 속하는지 확인하는 문제 | ||
*/ | ||
|
||
int main() { | ||
int n, m, a, b, input; // n: 도시의 수, m: 여행 계획에 속한 도시들의 수 | ||
|
||
//입력 | ||
cin >> n >> m; | ||
parent.assign(n + 1, -1); | ||
for (int i = 1; i <= n; i++) { | ||
for (int j = 1; j <= n; j++) { // i번째 줄의 j번째 수는 i번 도시와 j번 도시의 연결 정보 | ||
cin >> input; // 1이면 연결, 0이면 연결 안되어있음 | ||
if (input) //두 정점이 연결되어 있다면 | ||
unionInput(i, j); // Union 연산 | ||
} | ||
} | ||
cin >> a; //시작 정점 | ||
while (--m) { // 계획에 속한 도시들 수 줄여가면서 | ||
cin >> b; // 여행 계획 순서 | ||
if (findParent(a) != findParent(b)) { //서로 다른 집합이라면 -> 해당 경로 불가능 | ||
cout << "NO"; // No 출력 | ||
return 0; // 함수 종료 | ||
} | ||
} | ||
cout << "YES"; // 가능하므로 YES 출력 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// | ||
// Created by user on 2021-11-30. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
|
||
using namespace std; | ||
|
||
vector<int> parent; | ||
|
||
//Find 연산 | ||
int findParent(int node) { | ||
if (parent[node] < 0) //루트 정점 | ||
return node; // 입력한 노드가 루트 정점이므로 노드 반환 | ||
return parent[node] = findParent(parent[node]); //그래프 압축하며 루트 정점 찾기 | ||
} | ||
|
||
//Union 연산 | ||
bool unionInput(int x, int y) { | ||
int xp = findParent(x); // x의 루트 정점 | ||
int yp = findParent(y); // y의 루트 정점 | ||
|
||
if (xp == yp) //같은 집합에 있다면 유니온 할 수 없음 | ||
return false; // 할 수 없으므로 false 반환 | ||
if (parent[xp] < parent[yp]) { //새로운 루트 xp | ||
parent[xp] += parent[yp]; // xp에 yp 트리 추가 | ||
parent[yp] = xp; // yp의 루트 정점을 xp로 설정 | ||
} else { //새로운 루트 yp | ||
parent[yp] += parent[xp]; // yp에 xp 추가 | ||
parent[xp] = yp; // xp의 루트 정점을 yp로 설정 | ||
} | ||
return true; // Union 가능하므로 true 반환 | ||
} | ||
/** | ||
* 사이클이 발생한 순간 = 같은 집합에 있는 원소 두 개를 유니온하려 할 때 | ||
* unionInput 함수의 반환형을 bool로 선언하여 cycle이 생성되는 순간 발견하기 | ||
*/ | ||
int main() { | ||
int n, m, x, y; // n: 점의 개수, m: 게임 차례, x, y: 플레이어가 자신의 차례에 선택한 두 개의 점 | ||
|
||
//입력 | ||
cin >> n >> m; | ||
parent.assign(n, -1); // 점의 개수만큼 -1로 초기화 | ||
for (int i = 0; i < m; i++) { // 차례만큼 | ||
cin >> x >> y; // 선택한 두 개의 점 입력 | ||
|
||
//연산 & 출력 | ||
if (!unionInput(x, y)) { //유니온 할 수 없음 = 사이클이 생성됨 | ||
cout << i + 1; // i의 시작 값을 0으로 설정했으므로 사이클이 만들어진 차례의 번호는 i+1 | ||
return 0; // 종료 | ||
} | ||
} | ||
cout << 0; // m번의 차례를 모두 처리한 이후에도 종료되지 않았으므로 0 출력 | ||
} |