Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[유니온 파인드] 5월 24일 #15

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions #17 0524_유니온_파인드/BOJ_1043_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* 거짓말 : sample */

#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);
int yp = findParent(y);

if (xp == yp) {
return;
}
if (parent[xp] < parent[yp]) {
parent[xp] += parent[yp];
parent[yp] = xp;
} else {
parent[yp] += parent[xp];
parent[xp] = yp;
}
}

int liarParty(vector<int> &parties) {
int cnt = 0;
for (int i = 0; i < parties.size(); i++) {
if (findParent(parties[i]) != findParent(0)) {
cnt++;
}
}
return cnt;
}

/**
* [거짓말]
*
* 1. 각 사람들은 다양한 파티를 통해 연결됐다고 할 수 있음
* 2. 연결된 사람들은 같은 집합에 속함
* 3. 각 집합에 속한 사람들 중 한 명이라도 진실을 안다면 그 집합의 사람들이 속한 파티에는 거짓말을 할 수 없음
* -> 유니온 파인드로 사람들을 집합으로 묶은 뒤, 파티마다 거짓말을 할 수 있는지 확인하기
* -> 이때, 진실을 아는 사람들의 루트 정점을 0으로 설정해서 유니온 파인드를 통해 집합으로 묶고 시작
* -> 0과 같은 집합이 아니어야 거짓말을 할 수 있음
*
* !주의! 파티 정보를 입력받으며 바로 거짓말 가능 여부를 판단할 수 없음 (예제 입력 4)
* 각 파티에서 한 사람만 저장해둔 뒤, 마지막에 거짓말 가능 여부 한 번에 판단
*/

int main() {
int n, m;

//입력
cin >> n >> m;
parent.assign(n + 1, -1);

int init, p;
cin >> init;
while (init--) { //진실을 아는 사람들
cin >> p;
unionInput(0, p);
}

int cnt, first_person, person;
vector<int> parties;
while (m--) {
cin >> cnt >> first_person;

//연산
parties.push_back(first_person); //파티 정보로 각 파티의 첫번째 사람만 저장
while (--cnt) {
cin >> person;
unionInput(first_person, person);
}
}

//연산 & 출력
cout << liarParty(parties);
return 0;
}
124 changes: 124 additions & 0 deletions #17 0524_유니온_파인드/BOJ_12100_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* 2048 : sample */

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
typedef vector<vector<int>> matrix;

int n, ans = 0;

int getMaxBlock(matrix &board) {
int max_block = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
max_block = max(max_block, board[i][j]);
}
}
return max_block;
}

matrix transposeMatrix(matrix &board) {
matrix board_t(n, vector<int>(n, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
board_t[i][j] = board[j][i];
}
}
return board_t;
}

/**
* 상으로 이동하는 함수
* - 한 열씩 검사하면서 위의 행부터 2개씩 같은 거 있다면 합치기
* - 이때 블록 없는 부분은 넘어가고, 블록이 존재했던 값을 저장해서 비교하는 것이 중요!
*/
matrix upMove(matrix board) {
matrix temp(n, vector<int>(n, 0)); //새롭게 블록 저장할 배열
for (int j = 0; j < n; j++) {
int idx = 0;
int prev = 0;
for (int i = 0; i < n; i++) {
if (!board[i][j]) {
continue;
}
if (board[i][j] == prev) {
temp[idx - 1][j] *= 2;
prev = 0;
} else {
temp[idx++][j] = board[i][j];
prev = board[i][j];
}
}
}
return temp;
}

//백트래킹 탐색
void backtracking(int cnt, matrix board) {
if (cnt == 5) {
ans = max(ans, getMaxBlock(board));
return;
}
//Transpose matrix 구하기 (상->좌)
matrix board_t = transposeMatrix(board);
//상
backtracking(cnt + 1, upMove(board));
//하
reverse(board.begin(), board.end());
backtracking(cnt + 1, upMove(board));
//좌
backtracking(cnt + 1, upMove(board_t));
//우
reverse(board_t.begin(), board_t.end());
backtracking(cnt + 1, upMove(board_t));
}

/**
* [2048 (Easy)]
*
* - 상, 하, 좌, 우로 이동하는 경우에 대해 최대 5번 이동시키는 모든 경우를 구한 후, 가장 큰 블록 찾는 문제 - 백트래킹
* - 움직이는 함수는 하나만 짜고, 보드를 돌려가면서 상, 하, 좌, 우 모든 방향의 움직임을 만듦
*
* - 상 <-> 하: 행 순서를 뒤집어서 해결
* - 상/하 <-> 좌/우: Transpose Matrix 활용
*
* - ex. 2 2 1 를 상, 하, 좌, 우로 이동하는 경우 구하는 법
* 2 2 2
* 4 4 4
* -상: 원래 배열에서 상으로 움직이는 함수 실행
* 2 2 1 4 4 1
* 2 2 2 -> 4 4 2
* 4 4 4 0 0 4
* -하: 원래 배열의 행 순서를 뒤집은 후, 상으로 움직이는 함수 실행
* 2 2 1 4 4 4 4 4 4
* 2 2 2 -> 2 2 2 -> 4 4 2
* 4 4 4 2 2 1 0 0 1
* -좌: 원래 배열의 전치 행렬을 구한 후, 상으로 움직이는 함수 실행
* 2 2 1 2 2 4 4 4 8
* 2 2 2 -> 2 2 4 -> 1 2 4
* 4 4 4 1 2 4 0 0 0
* -우: 원래 배열의 전치 행렬에서 행 순서를 뒤집은 후, 상으로 움직이는 함수 실행
* 2 2 1 1 2 4 1 4 8
* 2 2 2 -> 2 2 4 -> 4 2 4
* 4 4 4 2 2 4 0 0 0
*/

int main() {
//입력
cin >> n;
matrix board(n, vector<int>(n, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> board[i][j];
}
}

//연산
backtracking(0, board);

//출력
cout << ans;
return 0;
}
43 changes: 43 additions & 0 deletions #17 0524_유니온_파인드/BOJ_16114_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* 화살표 연산자 : sample */

#include <iostream>

using namespace std;

string solution(int x, int n) {
if (n > 1 && n % 2 == 1) {
return "ERROR";
}
if (n == 1 && x < 0) {
return "INFINITE";
}
if (n == 1 || x <= 0) {
return "0";
}
if (n == 0) {
return "INFINITE";
}
return to_string((x - 1) / (n / 2));
}

/**
* [화살표 연산자]
*
* 1. n이 1보다 큰 홀수인 경우 -> ERROR
* 2. n이 1인데 x가 음수인 경우 -> while문 조건 항상 참 -> INFINITE
* 3. n이 1인데 x가 양수인 경우 or x가 0보다 작거나 같은 경우 -> while문에 진입 못함 -> 0
* 4. n이 0인데 x가 양수인 경우 -> while문 조건 항상 참 -> INFINITE
* 5. 나머지 경우엔 (x - 1)을 (n / 2)로 나눈 몫을 출력
* - 연산했을 때 1 이상이 남을 때까지만 출력을 할 수 있으므로 1을 뺀 값에서 몫을 구함
*/

int main() {
int x, n;

//입력
cin >> x >> n;

//연산 & 출력
cout << solution(x, n);
return 0;
}
60 changes: 60 additions & 0 deletions #17 0524_유니온_파인드/BOJ_20040_sample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* 사이클 게임 : sample */

#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);
int yp = findParent(y);

if (xp == yp) {
return false;
}
if (parent[xp] < parent[yp]) {
parent[xp] += parent[yp];
parent[yp] = xp;
} else {
parent[yp] += parent[xp];
parent[xp] = yp;
}
return true;
}

/**
* [사이클 게임]
*
* 사이클이 발생한 순간 = 같은 집합에 있는 원소 두 개를 유니온하려 할 때
* unionInput 함수의 반환형을 bool로 선언하여 cycle이 생성되는 순간 발견하기
*/

int main() {
int n, m, x, y;

//입력
cin >> n >> m;
parent.assign(n, -1);
for (int i = 0; i < m; i++) {
cin >> x >> y;

//연산 & 출력
if (!unionInput(x, y)) { //사이클이 생성됨
cout << i + 1;
return 0;
}
}
cout << 0;
return 0;
}
Loading