-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[최소 신장 트리] 11월 23일 -ing
- Loading branch information
Showing
7 changed files
with
545 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// | ||
// Created by user on 2021-12-06. | ||
// | ||
|
||
// | ||
// Created by iw040 on 2021/11/30. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
#include <queue> | ||
|
||
using namespace std; | ||
const int INF = 1e6; | ||
typedef pair<int, int> ci; | ||
|
||
int prim(int v, int start, vector<vector<ci>> &graph) { | ||
int sum = 0; // 가중치 변수 | ||
//vector<int> dist(v + 1, INF); //각 정점까지의 비용 (없어도 상관없으나, 사용하면 메모리를 좀 더 아낄 수 있음) | ||
vector<bool> visited(v + 1, false); //정점 방문 여부 (다익스트라와 달리 프림에선 필요!) | ||
priority_queue<ci, vector<ci>, greater<>> pq; // 우선순위큐 선언 | ||
|
||
//초기화 | ||
//dist[start] = 0; | ||
pq.push({0, start}); | ||
|
||
while (!pq.empty()) { // 큐가 비어있지 않을 동안 | ||
int weight = pq.top().first; //간선 가중치 | ||
int node = pq.top().second; //현재 정점 | ||
pq.pop(); // 맨 위에 있는 값 out | ||
|
||
if (visited[node]) //이미 확인했던 정점 | ||
continue; // pass | ||
sum += weight; //MST 간선 가중치 총 합에 더해줌 | ||
visited[node] = true; //방문 처리 | ||
|
||
for (int i = 0; i < graph[node].size(); i++) { // 그래프의 크기 동안 반복 | ||
int next_node = graph[node][i].first; // 다음 정점 | ||
int next_weight = graph[node][i].second; // 다음 간선 가중치 | ||
if (!visited[next_node]) { //미방문 정점(이면서 더 짧은 간선을 통해 갈 수 있다면) | ||
//dist[next_node] = next_weight; | ||
pq.push({next_weight, next_node}); // 큐에 삽입 | ||
} | ||
} | ||
} | ||
return sum; // 트리의 가중치 반환 | ||
} | ||
|
||
int main() { | ||
int v, e, a, b, c; | ||
// v: 정점의 개수, e: 간선의 개수 | ||
// a, b, c: 간선에 대한 정보 | ||
|
||
//입력 | ||
cin >> v >> e; // 정점과 간선의 개수 입력 | ||
vector<vector<ci>> graph(v + 1, vector<ci>(0)); // 정점 개수만큼의 크기를 가진 그래프 생성 | ||
while (e--) { //무방향 그래프 | ||
cin >> a >> b >> c; // 간선 정보 입력 | ||
graph[a].emplace_back(b, c); // a번 정점과 b번 정점이 가중치 c인 간선으로 연결 | ||
graph[b].emplace_back(a, c); // b번 정점과 a번 정점이 가중치 c인 간선으로 연결 | ||
} | ||
|
||
//연산 & 출력 | ||
cout << prim(v, 1, graph); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// | ||
// Created by user on 2021-12-07. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
#include <queue> | ||
|
||
using namespace std; | ||
const int INF = 1e5 + 1; | ||
|
||
int prim(int size, int start, vector<vector<int>> &graph) { | ||
int sum = 0; | ||
vector<int> dist(size, INF); //각 논까지의 비용 | ||
vector<bool> visited(size, false); //논 방문 여부 | ||
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq; // 우선수위 큐 선언 | ||
|
||
//초기화 | ||
dist[start] = 0; // 거리 초기화 | ||
pq.push({0, start}); // 정점 초기화 | ||
|
||
while (!pq.empty()) { | ||
int cost = pq.top().first; //간선 가중치 | ||
int cur = pq.top().second; //현재 논 | ||
pq.pop(); | ||
|
||
if (visited[cur]) //이미 확인했던 정점 | ||
continue; | ||
sum += cost; //MST 간선 가중치 총합 | ||
visited[cur] = true; //방문 처리 | ||
|
||
for (int i = 0; i < size; i++) { | ||
if (!visited[i] && graph[cur][i] < dist[i]) { //미방문 정점이면서 더 짧은 간선을 통해 갈 수 있다면 | ||
dist[i] = graph[cur][i]; // 해당 간선의 길이를 거리 계산에 추가 | ||
pq.push({dist[i], i}); // 큐에 해당 정점 추가 | ||
} | ||
} | ||
} | ||
return sum; // 간선 가중치 반환 | ||
} | ||
|
||
/** | ||
* 각 논들 사이의 간선도 고려하고, 우물을 파는 경우도 고려? -> 복잡 | ||
* 논에 추가로 모든 우물과 연결되는 수원이 있다고 가정! | ||
* ->직접 논에 우물을 파는 경우는 수원과 각 논 사이의 간선 가중치라고 할 수 있음 | ||
* | ||
* 0 2 2 2 5 | ||
* 2 0 3 3 4 | ||
* 2 3 0 4 4 | ||
* 2 3 4 0 3 | ||
* 5 4 4 3 0 | ||
* | ||
* 인덱스 0 ~ n-1은 논, 인덱스 n은 수원 | ||
* 1개 이상의 논은 반드시 직접 우물을 파야 하므로 수원(n)에서 시작하는 프림 알고리즘 | ||
*/ | ||
int main() { | ||
int n, w; | ||
|
||
cin >> n; // 논 개수 입력 | ||
vector<vector<int>> graph(n + 1, vector<int>(n + 1, 0)); // 논 크기의 그래프 생성 | ||
for (int i = 0; i < n; i++) { //수원으로부터 물을 끌어오는 비용 | ||
cin >> w; // 비용 입력 | ||
graph[i][n] = graph[n][i] = w; // 각 거리에 대해 비용 삽입 | ||
} | ||
|
||
for (int i = 0; i < n; i++) { // 논 크기만큼 반복 | ||
for (int j = 0; j < n; j++) // 논 크기만큼 반복 | ||
cin >> graph[i][j]; //논들 사이에서 물을 끌어오는 비용 | ||
} | ||
|
||
cout << prim(n + 1, n, graph); //수원에서 시작하는 프림 알고리즘 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// | ||
// Created by user on 2021-12-07. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
#include <queue> | ||
#include <deque> | ||
#include <tuple> | ||
#include <algorithm> | ||
|
||
using namespace std; | ||
typedef vector<vector<int>> matrix; | ||
typedef 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(); // 정보 저장된 죽은 나무는 큐에서 제거 | ||
land[row][col] += (age / 2); | ||
// 죽은 나무마다 나이를 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}; // 인접한 8개의 칸에 대한 row 좌표 | ||
int dc[8] = {0, 0, -1, 1, -1, 1, -1, 1}; // 인접한 8개의 칸에 대한 col 좌표 | ||
|
||
while (!breeding_tree.empty()) { // 번식하는 나무 큐에 있는 모든 나무에 대해서 | ||
int row = breeding_tree.front().first; //번식할 나무의 행 | ||
int col = breeding_tree.front().second; //번식할 나무의 열 | ||
breeding_tree.pop(); // 정보 저장된 번식할 나무는 큐에서 제거 | ||
|
||
for (int j = 0; j < 8; j++) { // 8개의 칸에 대해 | ||
int nr = row + dr[j]; // row 위치 이동 | ||
int nc = col + dc[j]; // col 위치 이동 | ||
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++) // 상도의 모든 땅에 | ||
for (int j = 0; j < n; j++) // 가로, 세로 반복하면서 | ||
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; | ||
// n: 땅의 한 변의 길이, m: 나무 개수, k: k년이 지난 후 | ||
matrix a(n, vector<int>(n, 0)); // 추가되는 양분, 초기 값 0으로 설정 | ||
matrix land(n, vector<int>(n, 5)); //처음 양분 모든 칸에 5 | ||
queue<pair<int, int>> breeding_tree; //번식할 트리 | ||
deque<tp> tree; // 나무의 정보를 담을 덱 생성 | ||
for (int i = 0; i < n; i++) // 땅의 넓이만큼 | ||
for (int j = 0; j < n; j++) // 반복하면서 | ||
cin >> a[i][j]; // 추가되는 양분 정보 입력 | ||
while (m--) { // 나무 개수만큼 반복하면서 | ||
cin >> x >> y >> z; // 나무의 정보 - x, y: 나무의 위치, z: 나무의 나이 | ||
tree.emplace_back(z, x - 1, y - 1); //(0, 0)부터 시작하도록 구현하기위해 1을 빼준 인덱스에 접근 | ||
} | ||
|
||
//연산 | ||
sort(tree.begin(), tree.end()); //어린 나이 순으로 정렬 | ||
while (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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// | ||
// Created by user on 2021-12-07. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
#include <map> | ||
|
||
using namespace std; | ||
typedef 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; // 현재 후보가 추천 횟수가 가장 작다면 cnt 수를 그 후보의 추천 횟수로 업데이트 | ||
t = cur_t; // 게시 시간도 업데이트 | ||
del = iter; // 해당 후보를 삭제 | ||
} else if (cur_cnt == cnt && cur_t < t) { //추천 횟수가 가장 작은 후보가 여러명이라면, 게시 시간이 오래된 후보 찾기 | ||
t = cur_t; // 게시 시간 업데이트하고 | ||
del = iter; // 해당 후보를 삭제 | ||
} | ||
} | ||
return del; // 삭제하는 후보 반환 | ||
} | ||
|
||
/** | ||
* 1. 비어있는 사진틀이 없는 경우, 가장 추천수가 작은 학생 중 게시 시간이 오래된 학생을 삭제 | ||
* 2. 후보 학생을 바로 찾기 위해 본 풀이는 map 컨테이너를 사용해 구현 | ||
* | ||
* !주의! 게시 시간 정보 저장 시, 후보로 올라간 가장 첫 시간을 저장. 이미 후보에 있는데 게시 시간이 갱신되지 않도록 주의. | ||
*/ | ||
|
||
int main() { | ||
int n, m, input; | ||
// n: 사진틀의 개수 | ||
// m: 전체 학생의 총 추천 횟수 | ||
|
||
//입력 & 연산 | ||
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 << ' '; | ||
} |
Oops, something went wrong.