diff --git "a/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/10775_re.cpp" "b/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/10775_re.cpp" new file mode 100644 index 0000000..1f16eca --- /dev/null +++ "b/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/10775_re.cpp" @@ -0,0 +1,61 @@ +// +// Created by user on 2021-11-30. +// + +#include +#include + +using namespace std; + +vector 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 출력 +} \ No newline at end of file diff --git "a/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/16236_re.cpp" "b/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/16236_re.cpp" new file mode 100644 index 0000000..1affae5 --- /dev/null +++ "b/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/16236_re.cpp" @@ -0,0 +1,114 @@ +// +// Created by user on 2021-11-30. +// + +#include +#include +#include +#include + +using namespace std; +const int INF = 401; // 공간의 최대 +typedef pair ci; // 상어의 위치 + +pair nextPos(int n, int shark_size, ci &shark, vector> &board) { + int dr[4] = {-1, 1, 0, 0}; + int dc[4] = {0, 0, -1, 1}; + // 순서대로 상, 하, 좌, 우 + + int min_dist = INF; // 최소 값의 초기값은 최대로 설정 + queue q; //상어가 갈 수 있는 곳 + vector> visited(n, vector(n, 0)); //상어의 방문 여부 + vector 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 &shark, vector> &board) { + int ans = 0, size = 2, cnt = 0; // size: 상어의 크기 (초기값 2로 설정), cnt: 먹은 물고기의 수 + while (true) { + pair 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 shark_pos; // 상어의 위치 + + //입력 + cin >> n; + vector> board(n, vector(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); +} \ No newline at end of file diff --git "a/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/1976_re.cpp" "b/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/1976_re.cpp" new file mode 100644 index 0000000..2076a42 --- /dev/null +++ "b/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/1976_re.cpp" @@ -0,0 +1,61 @@ +// +// Created by user on 2021-11-30. +// +#include +#include + +using namespace std; + +vector 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 출력 +} diff --git "a/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/20040_re.cpp" "b/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/20040_re.cpp" new file mode 100644 index 0000000..163ef51 --- /dev/null +++ "b/11\354\233\224 23\354\235\274 - \354\234\240\353\213\210\354\230\250 \355\214\214\354\235\270\353\223\234/20040_re.cpp" @@ -0,0 +1,55 @@ +// +// Created by user on 2021-11-30. +// + +#include +#include + +using namespace std; + +vector 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 출력 +} \ No newline at end of file