-
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.
Showing
5 changed files
with
404 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,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 << ' '; | ||
} |
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,101 @@ | ||
// | ||
// Created by user on 2021-12-07. | ||
// | ||
|
||
#include <iostream> | ||
#include <vector> | ||
#include <tuple> | ||
#include <queue> | ||
#include <cmath> | ||
|
||
using namespace std; | ||
typedef pair<double, double> ci; | ||
typedef tuple<double, int, int> tp; | ||
|
||
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]) { //새로운 루트 xp | ||
parent[xp] += parent[yp]; // xp에 yp 트리 추가 | ||
parent[yp] = xp; // yp의 루트 정점을 xp로 설정 | ||
} else { //새로운 루트 yp | ||
parent[yp] += parent[xp]; // yp에 xp 추가 | ||
parent[xp] = yp; // xp의 루트 정점을 yp로 설정 | ||
} | ||
return true; // 유니온 가능하다고 반환 | ||
} | ||
|
||
double kruskal(int v, priority_queue<tp, vector<tp>, greater<>> &pq) { | ||
int cnt = 0; // 사용한 간선 카운트 | ||
double sum = 0; // 간선의 가중치 | ||
|
||
while (cnt < v - 1) { //사용한 간선의 수가 v-1보다 적을 동안 | ||
double cost = get<0>(pq.top()); // 가중치 추가 | ||
int x = get<1>(pq.top()); // x의 좌표 저장 | ||
int y = get<2>(pq.top()); // y의 좌표 저장 | ||
|
||
pq.pop(); // 정보 입력한 간선 out | ||
if (unionInput(x, y)) { //유니온 했다면 | ||
cnt++; //사용된 간선 증가 | ||
sum += cost; //간선의 가중치 | ||
} | ||
} | ||
return sum; | ||
} | ||
|
||
/** | ||
* 4386번 : 별자리 만들기의 응용 문제 | ||
* 이미 연결된 정점들이 존재한다는 것을 제외하고는 4386번과 동일 | ||
* | ||
* 1. 임의의 두 별에 대한 거리(간선) 모두 구하기 | ||
* 2. 이미 존재하는 통로들 표시 | ||
* !주의! 통로의 개수가 m개라면 v-m-1개의 간선만 더 추가하면 될까? | ||
* 이미 연결된 통로들도 사이클을 이룰 수 있기 때문에 유니온 연산을 하며 사이클 없이 연결된 간선만 세기 | ||
* 3. 이미 연결된 통로의 수를 k개라고 하면 v-k-1개의 간선을 추가로 선택 | ||
*/ | ||
int main() { | ||
int n, m, a, b, v = 0; | ||
// n: 우주신들의 개수, m: 이미 연결된 통로의 수 | ||
priority_queue<tp, vector<tp>, greater<>> pq; // 우선순위 큐 | ||
|
||
//입력 | ||
cin >> n >> m; | ||
parent.assign(n + 1, -1); // -1로 초기화 | ||
vector<ci> stars(n + 1); // 별 개수만큼 벡터 생성 | ||
for (int i = 1; i <= n; i++) // n개의 별에 대해 | ||
cin >> stars[i].first >> stars[i].second; // 우주신들의 좌표 적기 | ||
|
||
|
||
//연산 | ||
//임의의 두 별에 대한 거리(간선) 모두 구하기 | ||
for (int i = 1; i <= n - 1; i++) { | ||
for (int j = i + 1; j <= n; j++) { | ||
double xd = stars[i].first - stars[j].first; //두 별사이 x축 거리 | ||
double yd = stars[i].second - stars[j].second; // 두 별 사이 y축 거리 | ||
pq.push({sqrt(xd * xd + yd * yd), i, j}); // 두 별 사이 거리를 큐에 추가 | ||
} | ||
} | ||
while (m--) { // 연결된 통로들에 대해 | ||
cin >> a >> b; // 통로 연결 정보 추가 | ||
if (unionInput(a, b)) //이미 연결된 통로 | ||
v++; | ||
} | ||
|
||
//연산 & 출력 | ||
cout << fixed; | ||
cout.precision(2); // 소수점 둘째자리까지 출력 | ||
cout << kruskal(n - v, pq); // 최소의 통로 길이 출력 | ||
} |
Oops, something went wrong.