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/1043.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/1043.cpp" new file mode 100644 index 0000000..08a23cc --- /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/1043.cpp" @@ -0,0 +1,90 @@ +// +// Created by user on 2021-11-30. +// + +#include +#include + +using namespace std; + +vector truth; //진실을 아는지 여부 +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 (truth[xp] || truth[yp]) //둘 중 하나라도 진실을 믿는 사람이 있다면 표시 + truth[xp] = truth[yp] = true; //둘다 진실을 믿는다고 표시 + if (parent[xp] < parent[yp]) { //새로운 루트 xp + parent[xp] += parent[yp]; + parent[yp] = xp; + } else { //새로운 루트 yp + parent[yp] += parent[xp]; + parent[xp] = yp; + } +} + +int liarParty(vector &parties) { + int cnt = 0; //과장할 수 있는 파티 개수 + for (int i = 0; i < parties.size(); i++) { + int root = findParent(parties[i]); //파티 집합의 루트 정점 + if (!truth[root]) + cnt++; //진실을 믿는 사람이 없으면 개수 늘려줌 + } + return cnt; //리턴 +} + +/** + * 1. 각 사람들은 다양한 파티를 통해 연결됐다고 할 수 있음 + * 2. 연결된 사람들은 같은 집합에 속함 + * 3. 각 집합에 속한 사람들 중 한 명이라도 진실을 안다면 그 집합의 사람들이 속한 파티에는 거짓말을 할 수 없음 + * -> 유니온 파인드로 사람들을 집합으로 묶은 뒤, 파티마다 거짓말을 할 수 있는지 확인하기 + * + * !주의! 파티 정보를 입력받으며 바로 거짓말 가능 여부를 판단할 수 없음 (예제 입력 4) + * 각 파티에서 한 사람만 저장해둔 뒤, 마지막에 거짓말 가능 여부 한 번에 판단 + */ +int main() { + int n, m; + + //입력 + cin >> n >> m; //사람 수 n, 파티 수 m + truth.assign(n + 1, false); //진실을 아는 사람 수 벡터 할당 + parent.assign(n + 1, -1); //파티 참여 사람 수 벡터 할당 + + int init, p; + cin >> init; //진실을 아는 사람 수 + while (init--) { //진실을 아는 사람들 + cin >> p; //번호 넣어줌 + truth[p] = true; //진실을 앎을 표시 + } + + int cnt, first_person, people; + vector parties; + while (m--) { + cin >> cnt >> first_person; + //파티에 오는 사람의 수와 첫번째 사람의 번호 + + //연산 + parties.push_back(first_person); //파티 정보로 각 파티의 첫번째 사람만 저장 + while (--cnt) { + cin >> people; //파티에 오는 사람들 번호 + unionInput(first_person, people); //유니온 + } + } + + int ans = liarParty(parties); //과장 파티 개수 리턴해주는 함수 호출 + + //출력 + cout << ans; +} 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.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.cpp" new file mode 100644 index 0000000..2f9ade6 --- /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.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(); //큐에서 뺌 + + 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; //거리 갱신 + 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; //답, 사이즈, 물고기 개수 + while (true) { + pair result = nextPos(n, size, shark, board); + //이동 값 + if (result.first == INF) //더 이상 먹을 수 있는 물고기가 공간에 없음 + break; //탈출 + ans += result.first; //이동 시간 더해줌 + cnt++; //물고기 개수 늘림 + if (cnt == size) { //상어 크기 증가 + cnt = 0; //다시 초기화 + size++; //상어크기도 늘려줌 + } + + //상어 이동 + ci pos = result.second; //이동값에 따라 이동 시켜줌 + board[shark.first][shark.second] = 0; //이동한 곳은 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; + pair shark_pos; + + //입력 + cin >> n; //공간의 크기 + vector> board(n, vector(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); +} 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/16562.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/16562.cpp" new file mode 100644 index 0000000..db2747e --- /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/16562.cpp" @@ -0,0 +1,67 @@ +// +// 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; //리턴 + //앞선 라이브코딩 코드와 달리 parent[xp]가 클때 + if (parent[xp] > parent[yp]) //새로운 루트 xp + //앞선 라이브코딩 코드와 달리 parent[xp] += parent[yp] 해주는 과정 없음 + parent[yp] = xp; + else //새로운 루트 yp + parent[xp] = yp; +} + +int friendshipCost(int n) { + int sum = 0; //합 초기화 + for (int i = 1; i <= n; i++) { + if (parent[i] < 0) //루트 정점이라면 + sum += -parent[i]; //음수인 루트값(친구비)를 양수로 바꿔서 더해줌 + } + return sum; //합 리턴 +} + +int main() { + int n, m, k, v, w, cost; + + //입력 + cin >> n >> m >> k; //학생수, 친구관계 수, 가지고 있는 돈 + parent.assign(n + 1, 0); //부모 벡터 할당 + for (int i = 1; i <= n; i++) { + cin >> cost; //친구비 + parent[i] = -cost; //루트 정점에 필요한 친구비(음수)를 저장 + } + + //연산 + while (m--) { + cin >> v >> w; //학생v와 학생w가 서로 친구 + unionInput(v, w); //유니온 + } + + int ans = friendshipCost(n); //친구비 계산 + + //출력 + if (ans <= k) //k보다 작으면(친구를 다 사귈 수 있으면) + cout << ans; //답 출력 + else + cout << "Oh no"; //아니면 oh no +} 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/1717.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/1717.cpp" new file mode 100644 index 0000000..6a34299 --- /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/1717.cpp" @@ -0,0 +1,59 @@ +// +// 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]; + parent[yp] = xp; + } else { //새로운 루트 yp + parent[yp] += parent[xp]; + parent[xp] = yp; + } +} + +int main() { + ios::sync_with_stdio(false); + cin.tie(NULL); + + int n, m, cmd, a, b; + + //입력 + cin >> n >> m; //n+1개의 집합, m개의 연산개수 + parent.assign(n + 1, -1); //집합 벡터 할당 + while (m--) { + cin >> cmd >> a >> b; //a가 포함되어 있는 집합과 b가 포함되어있는 집합을 + //합치거나(0) 확인함(1) + + //연산 + if (cmd == 0) { //유니온 + unionInput(a, b); + } + if (cmd == 1) { //파인드 + if (findParent(a) == findParent(b)) + cout << "YES\n"; //같은 집합이면 + else + cout << "NO\n"; //다른 집합이면 + } + } +} 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/18111.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/18111.cpp" new file mode 100644 index 0000000..ccb1831 --- /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/18111.cpp" @@ -0,0 +1,63 @@ +// +// Created by user on 2021-11-30. +// + +#include +#include + +using namespace std; +const int INF = 1e9; + +int mineLand(int n, int m, int b, int height, vector> &land) { + int tot_time = 0; //토탈 시간 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (land[i][j] > height) { //블록 제거 + b += (land[i][j] - height); //제거하는 블록 개수 + tot_time += 2 * (land[i][j] - height); //걸리는 시간 + } else if (land[i][j] < height) { //블록 쌓기 + b -= (height - land[i][j]); //쌓아주는 블록 개수 + tot_time += (height - land[i][j]); //걸리는 시간 + } + } + } + if (b < 0) //최종적으로 블럭이 음수면 불가능한 높이 + return INF + 1; //불가능한 값 리턴 + return tot_time; //양수면 값 리턴 +} + +/** + * 1. 가장 낮은 땅의 높이를 h라고 할 때, h-1의 높이를 만드는건 h보다 2*(N*M)의 시간이 더 소요됨 + * 2. 가장 높은 땅의 높이를 h라고 할 때, h+1의 높이를 만드는건 h보다 (N*M)의 시간이 더 소요됨 + * -> 따라서 땅의 높이가 될 수 있는 후보는 (가장 낮은 땅) ~ (가장 높은 땅) + * -> 가능한 모든 높이에 대해 브루트포스 연산해도 시간 초과 X + * + * !주의! 당장 쌓을 블록이 없더라도 언젠가 다른 곳의 블록을 제거해서 쌓을 수 있음. + */ +int main() { + int n, m, b, min_height = 256, max_height = 0; + + //입력 + cin >> n >> m >> b; //세로, 가로, 인벤토리내 블록 개수 + vector> land(n, vector(m, 0)); //land 벡터 할당 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + cin >> land[i][j]; //값 할당 + min_height = min(min_height, land[i][j]); //제일 낮은 땅 높이 + max_height = max(max_height, land[i][j]); //제일 높은 땅 높이 + } + } + + //연산 + int min_time = INF, height = 0; + for (int i = min_height; i <= max_height; i++) { + int t = mineLand(n, m, b, i, land); //시간 구하기 + if (t <= min_time) { //가장 빨리 작업이 끝나는 높이 + min_time = t; //시간 갱신 + height = i; //높이 갱신 + } + } + + //출력 + cout << min_time << ' ' << height; +} \ 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.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.cpp" new file mode 100644 index 0000000..2cb2920 --- /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.cpp" @@ -0,0 +1,62 @@ +// +// 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]; + parent[yp] = xp; + } else { //새로운 루트 yp + parent[yp] += parent[xp]; + parent[xp] = yp; + } +} + +/** + * 입력으로 주어지는 i, j 도시의 연결정보를 통해 서로소 집합을 만든 후, + * 여행 계획으로 세운 도시들이 모두 같은 집합에 속하는지 확인하는 문제 + */ + +int main() { + int n, m, a, b, input; + + //입력 + cin >> n >> m; //도시의 수 n, 여행 계획에 속한 도시들의 수 m + parent.assign(n + 1, -1); //집합 부모 벡터 할당 + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + cin >> input; //n개의 도시들 입력 + if (input) //두 정점이 연결되어 있다면 + unionInput(i, j); //유니온 + } + } + cin >> a; //시작 정점 + while (--m) { + cin >> b; //여행 계획 도시 + if (findParent(a) != findParent(b)) { //서로 다른 집합이라면 -> 해당 경로 불가능 + cout << "NO"; //불가능 출력 + return 0; //끝 + } + } + cout << "YES"; //가능 출력 +} \ 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/20040.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.cpp" new file mode 100644 index 0000000..895e117 --- /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.cpp" @@ -0,0 +1,56 @@ +// +// 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; //싸이클이 있음을 알림 + if (parent[xp] < parent[yp]) { //새로운 루트 xp + parent[xp] += parent[yp]; + parent[yp] = xp; + } else { //새로운 루트 yp + parent[yp] += parent[xp]; + parent[xp] = yp; + } + return true; //싸이클 없음 +} + +/** + * 사이클이 발생한 순간 = 같은 집합에 있는 원소 두 개를 유니온하려 할 때 + * unionInput 함수의 반환형을 bool로 선언하여 cycle이 생성되는 순간 발견하기 + */ +int main() { + int n, m, x, y; + + //입력 + cin >> n >> m; //평면상의 점 n개, m번째 차례 + parent.assign(n, -1); //집합의 부모 벡터 할당 + for (int i = 0; i < m; i++) { + cin >> x >> y; //i번째 차례에 플레이어가 선택한 두 점의 번호 + + //연산 & 출력 + if (!unionInput(x, y)) { //유니온 할 수 없음 = 사이클이 생성됨 + cout << i + 1; //차례 출력 + return 0; //끝 + } + } + cout << 0; //다 유니온 가능하면 사이클이 없으므로 0 출력 +} \ 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/4803.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/4803.cpp" new file mode 100644 index 0000000..40521db --- /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/4803.cpp" @@ -0,0 +1,83 @@ +// +// Created by user on 2021-11-30. +// + + +#include +#include + +using namespace std; + +vector cycle; +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) { //같은 집합에 있다면 유니온 할 수 없음 + cycle[xp] = true; //cycle이 있음 표시 + return; + } + if (cycle[xp] || cycle[yp]) //둘 중 하나라도 사이클이 있으면 표시 + cycle[xp] = cycle[yp] = true; //둘다 사이클로 표시 + if (parent[xp] < parent[yp]) { //새로운 루트 xp + parent[xp] += parent[yp]; + parent[yp] = xp; + } else { //새로운 루트 yp + parent[yp] += parent[xp]; + parent[xp] = yp; + } +} + +int cntTree(int n) { + int cnt = 0; //트리개수 초기화 + for (int i = 1; i <= n; i++) { + if (parent[i] < 0 && !cycle[i]) //루트 정점인데 사이클도 없을 때 + cnt++; //트리개수 늘려줌 + } + return cnt; //트리개수 반환 +} + +void printResult(int num, int cnt) { + cout << "Case " << num; + if (cnt == 0) //0개면 tree없음 + cout << ": No trees.\n"; + else if (cnt == 1) //1개면 하나 + cout << ": There is one tree.\n"; + else //여러개면 몇개인지 출력 + cout << ": A forest of " << cnt << " trees.\n"; +} + +int main() { + int n, m, a, b, test_case = 0; + + while (true) { + test_case++; //테스트 케이스 추가 + + //입력 + cin >> n >> m; //정점의 개수 n, 간선의 개수 m + if (n == 0 && m == 0) //종료 조건 + break; + parent.assign(n + 1, -1); //부모 벡터 할당 + cycle.assign(n + 1, false); //싸이클 판별 벡터 할당 + while (m--) { //무방향 그래프 + cin >> a >> b; //간선 + unionInput(a, b); //유니온 + } + + //연산 + int tree_cnt = cntTree(n); //트리개수 세기 + + //출력 + printResult(test_case, tree_cnt); //결과 출력 + } +} \ No newline at end of file