Skip to content
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
85 changes: 85 additions & 0 deletions [MST]1207-추가제출/16202.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <iostream> //표준 입출력 스트림 사용
#include <vector> //vector
#include <tuple> //tuple

using namespace std; //표준 네임스페이스 사용
typedef tuple<int, int, int> tp; //tuple<int, int, int>타입을 tp로 정의함.

vector<int> 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<tp> &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<tp> 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의 가중치 출력함.
}
}
116 changes: 116 additions & 0 deletions [MST]1207-추가제출/16235.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include <iostream> //표준 입력스트림 사용
#include <vector> //vector
#include <queue> //queue
#include <deque> //deque
#include <tuple> //tuple
#include <algorithm> //sort

using namespace std; //표준 네임스페이스 사용
typedef vector<vector<int>> matrix; //정수형 2차원 벡터 matrix로 정의함
typedef tuple<int, int, int> tp; //tuple<int, int, int>를 tp로 정의함

queue<tp> spring(matrix &land, deque<tp> &tree, queue<pair<int, int>> &breeding_tree) { //땅의 양분 정보, 산 나무, 번식할 나무
queue<tp> 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<tp> &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<tp> &tree, queue<pair<int, int>> &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<int>(n, 0)); //로봇이 겨울에 주는 양분의 양
matrix land(n, vector<int>(n, 5)); //처음 양분 모든 칸에 5
queue<pair<int, int>> breeding_tree; //번식할 트리
deque<tp> 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<tp> dead_tree = spring(land, tree, breeding_tree); //봄이 지나고 죽은 나무
summer(dead_tree, land); //죽은 나무가 양분으로 변함
fall(n, tree, breeding_tree); //나무가 번식함
winter(n, a, land); //땅에 양분을 더함.
}

//출력
cout << tree.size(); //살아 있는 나무 수 출력함
}
53 changes: 53 additions & 0 deletions [MST]1207-추가제출/1713.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include <iostream> //표준 입력스트림 사용
#include <vector> //vector
#include <map> //map

using namespace std; //표준 네임스페이스 사용
typedef pair<int, int> ci; //pair<int, int>형을 ci로 정의함

map<int, ci>::iterator delCandidate(map<int, ci> &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<int, ci> 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 << ' '; //득표수 출력함
}