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

0524_유니온파인드_과제 #13

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 0524_유니온파인드_과제/1043.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#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]) { //크기가 작은 경우가 더 높이가 큰 노드.
parent[xp] += parent[yp]; //새로운 루트 xp 추가
parent[yp] = xp;
} else {//높이가 더 작은 노드
parent[yp] += parent[xp]; //새로운 루트 yp 추가
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); //각각이 자기 자신이 정점인 루트 정점 배열 선언 및 초기화
//1번부터 n번까지 접근

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;
}
122 changes: 122 additions & 0 deletions 0524_유니온파인드_과제/12100.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#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) { //행렬 transpose
matrix board_t(n, vector<int>(n, 0)); //transpose된 행렬 따로 선언 및 초기화
for (int i = 0; i < n; i++) { //t행렬의 행
for (int j = 0; j < n; j++) { //t행렬의 열
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]) { //0이 아닌 값이 있으면
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) { //최대 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]; //0은 빈칸, 이외의 값은 블록을 나타냄
}
}

//연산
backtracking(0, board);

//출력
cout << ans;
return 0;
}
42 changes: 42 additions & 0 deletions 0524_유니온파인드_과제/16114.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <iostream>

using namespace std;

string solution(int x, int n) { //변수 초기값과 정수 인수로 받음 ,
//프로그램의 실행 결과의 타입은 문자열
if (n > 1 && n % 2 == 1) { //n이 1보다 큰 홀수인 경우 = 2로 나눈 나머지가 1 : ERROR
return "ERROR";
}
if (n == 1 && x < 0) { //n이 1인데 x가 음수인 경우 while문 조건 항상 참 : INFINITE
return "INFINITE";
}
if (n == 1 || x <= 0) { // n이 1인데 x가 양수인 경우 or x가 0보다 작거나 같은 경우 : while문 빠져 나감
return "0";
}
if (n == 0) { //n이 0인데 x가 양수인 경우는 while문 조건 항상 참이 됨 : INFINITE
return "INFINITE";
}
return to_string((x - 1) / (n / 2)); //1이상 남을 떄까지만 출력 하므로, 1을 뺀 값에서 몫을 구함
}

/**
* [화살표 연산자]
*
* 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; //변수의 초기 값, 정수 N

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

//연산 & 출력
cout << solution(x, n); //변수 초기값과 정수 인수로 받음
return 0;
}
58 changes: 58 additions & 0 deletions 0524_유니온파인드_과제/20040.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#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]) { //크기가 작은 경우가 더 높이가 큰 노드.
parent[xp] += parent[yp]; //새로운 루트 xp 추가
parent[yp] = xp;
} else {//높이가 더 작은 노드
parent[yp] += parent[xp]; //새로운 루트 yp 추가
parent[xp] = yp;
}
return true; //다른 노드일경우 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;
}
79 changes: 79 additions & 0 deletions 0524_유니온파인드_과제/4195.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include <iostream>
#include <vector>
#include <map>

using namespace std;
const int MAX = 2e5; //친구 관계가 모두 다른 사용자로 들어왔을 때의 정점 최댓값

vector<int> parent(MAX + 1, -1); // 부모노드 담을 배열
map<string, int> people; //사용자의 아이디와 친구의 수 담을 맵 자료구조

//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]) { //크기가 작은 경우가 더 높이가 큰 노드.
parent[xp] += parent[yp]; //새로운 루트 xp 추가
parent[yp] = xp;
} else {//높이가 더 작은 노드
parent[yp] += parent[xp]; //새로운 루트 yp 추가
parent[xp] = yp;
}
}

/**
* [친구 네트워크]
*
* 1. weighted union find -> 루트 정점에 친구의 수(집합 원소 수) 저장
* 2. 친구 네트워크에 몇 명 있는지 구하기 -> 루트 정점의 parent값 접근
*
* !주의! 정점이 문자열로 들어오므로 map을 활용해 string을 int로 매핑
*/

int main() {
ios_base::sync_with_stdio(false); //입출력 속도 향상하기 위함.
cin.tie(NULL);
cout.tie(NULL);

int t, f; //테스트 케이스의 개수, 친구 관계의 수
string a, b; //친구 관계인 두 사용자의 아이디

//입력
cin >> t;
while (t--) { //반복되는 테스트 케이스만큼
int idx = 1; //정점과 매핑할 수
parent.assign(MAX + 1, -1); //각각 자기 자신이 정점인 배열로 초기화

cin >> f; //친구관계의 수 입력
while (f--) { //각 친구관계 마다
cin >> a >> b; //두 사용자의 아이디 입력
if (!people[a]) { //
people[a] = idx++; //친구의 수 증가시킴.
}
if (!people[b]) {
people[b] = idx++; //친구의 수 증가시킴.
}

//연산
int x = people[a], y = people[b]; //a와 b 친구관계 저장
unionInput(x, y); //유니온 연산

//출력
cout << -parent[findParent(x)] << '\n';
}
}
return 0;
}