diff --git "a/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/16202.cpp" "b/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/16202.cpp" new file mode 100644 index 0000000..164d74b --- /dev/null +++ "b/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/16202.cpp" @@ -0,0 +1,85 @@ +#include //표준 입출력 스트림 사용 +#include //vector +#include //tuple + +using namespace std; //표준 네임스페이스 사용 +typedef tuple tp; //tuple타입을 tp로 정의함. + +vector parent; //kruskal 알고리즘에서 부모 노드를 입력할 벡터 + +//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의 부모 노드를 xp에 저장 + int yp = findParent(y); //y의 부모 노드를 xp에 저장 + + if (xp == yp) //같은 집합에 있다면 유니온 할 수 없음 + return false; //false 리턴함 + if (parent[xp] < parent[yp]) { //새로운 루트 xp + parent[xp] += parent[yp]; //새로운 루트에 다른 루트의 자식 옮김. + parent[yp] = xp; //루트 노드였던 parent[yp]를 xp의 자식 노드로 바꿈. + } else { //새로운 루트 yp + parent[yp] += parent[xp]; //새로운 루트에 다른 루트의 자식 옮김. + parent[xp] = yp; //루트 노드였던 parent[xp]를 yp의 자식 노드로 바꿈. + } + return true; //true 리턴함. +} + +int kruskal(int n, int idx, vector &edges) { //크루스칼 알고리즘. + int cnt = 0, sum = 0; //사용된 간선의 수, 더해진 가중치 저장 변수 + for (int i = idx; i < edges.size(); i++) { //간선 하나씩 제거되는 + if (cnt == n - 1) //n-1개의 간선을 모두 연결함 + break; //반복문 탈출함. + int dist = get<0>(edges[i]); //가중치 대입함. + int x = get<1>(edges[i]); //정점 x와 + int y = get<2>(edges[i]); //정점 y를 대입함. + + if (unionInput(x, y)) { //연결이 되었다면 + cnt++; //사용된 간선 수 증가 + sum += dist; //가중치 더함 + } + } + if (cnt < n - 1) //mst를 만들 수 없음 + return 0; //0 리턴함. + return sum; //가중치 합 리턴함. +} + +/** + * MST 알고리즘을 여러 번 실행해도 될까? + * 1. 크루스칼 알고리즘의 시간 복잡도는 O(ElogE) + * 이는 오직 간선을 정렬하는 연산의 시간 복잡도! + * 즉, 모든 간선을 한 번 정렬해서 저장해두면 이후 몇 번의 알고리즘을 수행하여도 연산 시간에 큰 영향이 없음 + * 2. 간선 재사용을 위해 우선순위 큐가 아닌 벡터에 저장하고 크루스칼 알고리즘 k번 실행 + * 3. 매번 크루스칼을 수행할 때마다 제일 먼저 추가한 간선을 제외함 + * -> 제외될 간선은 배열의 0번째 간선부터 1, 2, 3번째 간선으로 순차적 제외 + * 4. 만약 한 번 MST를 만들 수 없다는게 확인됐다면 이후에도 MST를 만들 수 없으므로 flag 변수로 불필요한 연산 절약 + */ +int main() { + int n, m, k, x, y; //정점 수, 간선 수, 반복할 턴 수, 연결될 두 정점 + + cin >> n >> m >> k; //정점, 간선 수, 반복할 턴 수 입력받음 + vector edges; //재사용할거라 우선순위 큐가 아닌 벡터에 저장 + for (int i = 1; i <= m; i++) { //간선 수만큼 + cin >> x >> y; //두 정점 입력받음 + edges.emplace_back(i, x, y); //edges 벡터에 입력함. + } + + bool flag = false; //mst 만들 수 있는지 체크함. + for (int i = 0; i < k; i++) { //k번만큼 + if (flag) { //더이상 mst를 만들 수 없음 + cout << 0 << ' '; //0 출력함. + continue; //다음 반복 계속함. + } + parent.assign(n + 1, -1); //초기화 + int ans = kruskal(n, i, edges); //i번째 간선부터만 있는 그래프의 mst를 찾음. + if (ans == 0) //mst가 없다면 + flag = true; //true 체크함. + cout << ans << ' '; //mst의 가중치 출력함. + } +} diff --git "a/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/16235.cpp" "b/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/16235.cpp" new file mode 100644 index 0000000..f99b690 --- /dev/null +++ "b/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/16235.cpp" @@ -0,0 +1,116 @@ +#include //표준 입력스트림 사용 +#include //vector +#include //queue +#include //deque +#include //tuple +#include //sort + +using namespace std; //표준 네임스페이스 사용 +typedef vector> matrix; //정수형 2차원 벡터 matrix로 정의함 +typedef tuple tp; //tuple를 tp로 정의함 + +queue spring(matrix &land, deque &tree, queue> &breeding_tree) { //땅의 양분 정보, 산 나무, 번식할 나무 + queue dead_tree; //죽은 나무 저장할 큐 + int size = tree.size(); //살아있는 나무들 수 + while (size--) { //모든 나무 검사 + int age = get<0>(tree.front()); //나이 + int row = get<1>(tree.front()); //행 + int col = get<2>(tree.front()); //열 + tree.pop_front(); //기존 나무 정보 없앰 + + if (land[row][col] < age) { //자신의 나이만큼 양분을 먹을 수 없다면 + dead_tree.push({age, row, col}); //죽은 나무에 추가함 + continue; //다음 반복문 계속함 + } + land[row][col] -= age; //나이만큼 양분 먹음 + tree.emplace_back(age + 1, row, col); //나이 한살 더해서 살아 있는 나무가 더해짐. + if ((age + 1) % 5 == 0) //나이가 5의 배수라면 + breeding_tree.push({row, col}); //번식할 나무에 더해줌 + } + return dead_tree; //죽은 나무들을 반환함 +} + +void summer(queue &dead_tree, matrix &land) { //죽은 나무, 땅의 양분 정보 벡터 + while (!dead_tree.empty()) { //죽은 모든 나무에 대해 + int age = get<0>(dead_tree.front()); //죽은 나무의 나이 + int row = get<1>(dead_tree.front()); //죽은 나무의 행 위치 + int col = get<2>(dead_tree.front()); //죽은 나무의 열 위치 + dead_tree.pop(); //pop함 + land[row][col] += (age / 2); //나이의 절반이 양분이 됨. + } +} + +void fall(int n, deque &tree, queue> &breeding_tree) { //땅 크기, 산 나무 정보, 번식할 나무 큐 + int dr[8] = {-1, 1, 0, 0, -1, -1, 1, 1}; //상, 하, 좌, 우, 왼위, 왼아래, 오른위, 오른아래 + int dc[8] = {0, 0, -1, 1, -1, 1, -1, 1}; //상, 하, 좌, 우, 왼위, 왼아래, 오른위, 오른아래 + + while (!breeding_tree.empty()) { //번식할 큐에 있는 모든 나무 + int row = breeding_tree.front().first; //번식할 나무의 행 + int col = breeding_tree.front().second; //번식할 나무의 열 + breeding_tree.pop(); //pop함. + + for (int j = 0; j < 8; j++) { //인접한 8칸에 + int nr = row + dr[j]; //행 위치 정보 + int nc = col + dc[j]; //열 위치 정보 + if (nr < 0 || nr >= n || nc < 0 || nc >= n) //범위 내에 없다면 + continue; //다음 반복문 계속함 + tree.push_front({1, nr, nc}); //새로 생긴 나무 + } + } +} + +void winter(int n, matrix &a, matrix &land) { //땅 크기, 더해줄 양분 양, 땅의 양분 + for (int i = 0; i < n; i++) //n행만큼 + for (int j = 0; j < n; j++) //n열만큼 + land[i][j] += a[i][j]; //양분 더해줌. +} + +/** + * [문제 설명] - 단순 구현 문제 + * 봄: 하나의 칸마다 나이가 어린 나무부터 자신의 나이만큼 양분을 먹고, 나이가 1 증가함 + * 각 칸에 양분이 부족해 자신의 나이만큼 양분을 못 먹는 나무는 즉시 죽음 + * 여름: 봄에 죽은 나무가 양분으로 변함. 죽은 나무마다 나이를 2로 나눈 값이 양분으로 추가 (소수점 버림) + * 가을: 나이가 5의 배수인 나무가 번식. 인접한 8개 칸에 나이가 1인 나무가 생김 + * 겨울: 로봇(S2D2)이 땅을 돌아다니면서 A[r][c]만큼 각 칸에 양분 추가 + * + * K년이 지난 후 상도의 땅에 살아있는 나무의 개수 + * + * [문제 풀이] + * a: 로봇(S2D2)가 겨울에 주는 양분의 양 + * land: 땅의 양분 + * breeding_tree: 나이가 5의 배수인 트리 (번식할 트리) + * tree: 땅에 심은 나무 나이, 행, 열 정보 + * -> deque 컨테이너를 활용해 번식한 나무를 앞에 넣어주면 입력 후에만 정렬해서 사용 가능 + * + * 문제의 설명대로 계절별 연산을 진행 + */ + +int main() { + int n, m, k, x, y, z; //땅 크기, 나무 개수, 년 수, 나무 위치, 나무의 나이 + + //입력 + cin >> n >> m >> k; //땅, 나무, 년 크기 입력 받음. + matrix a(n, vector(n, 0)); //로봇이 겨울에 주는 양분의 양 + matrix land(n, vector(n, 5)); //처음 양분 모든 칸에 5 + queue> breeding_tree; //번식할 트리 + deque tree; //나무 정보 tuple 덱 + for (int i = 0; i < n; i++) //n행만큼 + for (int j = 0; j < n; j++) //n열만큼 + cin >> a[i][j]; //양분의 양 입력받음. + while (m--) { //나무 수만큼 + cin >> x >> y >> z; //위치 정보와 나이 입력받음. + tree.emplace_back(z, x - 1, y - 1); //(0, 0)부터 시작하도록 구현하기위해 1을 빼준 인덱스에 접근 + } + + //연산 + sort(tree.begin(), tree.end()); //어린 나이 순으로 정렬 + while (k--) { //k년동안 + queue dead_tree = spring(land, tree, breeding_tree); //봄이 지나고 죽은 나무 + summer(dead_tree, land); //죽은 나무가 양분으로 변함 + fall(n, tree, breeding_tree); //나무가 번식함 + winter(n, a, land); //땅에 양분을 더함. + } + + //출력 + cout << tree.size(); //살아 있는 나무 수 출력함 +} diff --git "a/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/1713.cpp" "b/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/1713.cpp" new file mode 100644 index 0000000..70b4352 --- /dev/null +++ "b/[MST]1207-\354\266\224\352\260\200\354\240\234\354\266\234/1713.cpp" @@ -0,0 +1,53 @@ +#include //표준 입력스트림 사용 +#include //vector +#include //map + +using namespace std; //표준 네임스페이스 사용 +typedef pair ci; //pair형을 ci로 정의함 + +map::iterator delCandidate(map &candidate) { //후보자 정보 + auto del = candidate.begin(); //처음 후보를 삭제한다 가정 + int cnt = candidate.begin()->second.first; //처음 후보의 추천 횟수 + int t = candidate.begin()->second.second; //처음 후보의 게시 시간 + for (auto iter = ++candidate.begin(); iter != candidate.end(); iter++) { //게시판 두 번째 후보자부터 마지막 후보자에 대해 + int cur_cnt = iter->second.first; //비교할 추천수 대입 + int cur_t = iter->second.second; //비교할 게시 시간 대앱 + if (cur_cnt < cnt) { //추천 횟수가 가장 작은 후보 찾기 + cnt = cur_cnt; //최소 추천수 갱신 + t = cur_t; //게시 시간 갱신 + del = iter; //삭제할 후보 갱신 + } else if (cur_cnt == cnt && cur_t < t) { //추천 횟수가 가장 작은 후보가 여러명이라면, 게시 시간이 오래된 후보 찾기 + t = cur_t; //게시 시간 갱신 + del = iter; //삭제할 후보 갱신 + } + } + return del; //삭제할 후보 가리키는 iterator 리턴함 +} + +/** + * 1. 비어있는 사진틀이 없는 경우, 가장 추천수가 작은 학생 중 게시 시간이 오래된 학생을 삭제 + * 2. 후보 학생을 바로 찾기 위해 본 풀이는 map 컨테이너를 사용해 구현 + * + * !주의! 게시 시간 정보 저장 시, 후보로 올라간 가장 첫 시간을 저장. 이미 후보에 있는데 게시 시간이 갱신되지 않도록 주의. + */ + +int main() { + int n, m, input; //사진틀 수, 총 투표수, 학생 번호 받을 변수 + + //입력 & 연산 + cin >> n >> m; //사진틀, 총 투표수 입력받음 + map candidate; //first: 후보 학생, second: {추천 횟수, 게시 시간} + for (int i = 0; i < m; i++) { //총 투표수만큼 + cin >> input; //학생 번호 입력받음 + if (candidate.size() == n && candidate.find(input) == candidate.end()) //비어있는 사진틀이 없는 경우 + candidate.erase(delCandidate(candidate)); //조건에 따라 후보 지움 + + if (candidate.find(input) == candidate.end()) //첫 게시라면 + candidate[input].second = i; //게시 시간 입력함. + candidate[input].first++; //추천 횟수 증가 + } + + //출력 + for (auto iter = candidate.begin(); iter != candidate.end(); iter++) //게시된 모든 후보자들의 + cout << iter->first << ' '; //득표수 출력함 +}