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

1207 위상정렬 추가제출 #19

Open
wants to merge 2 commits into
base: master
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
74 changes: 74 additions & 0 deletions 1130 - MST/1368.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// Created by 반예원 on 2021/12/06.
//

#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; //합계 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; //처음 start 값 0으로 초기화
pq.push({0, start});
// pq 우선순위큐에 값 저장
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}); //i와 dist값 저장
}
}
}
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));
// 그래프 2차벡터로 초기화
for (int i = 0; i < n; i++) { //수원으로부터 물을 끌어오는 비용
cin >> w; // 비용 입력받기
graph[i][n] = graph[n][i] = w; // 해당 그래프에 비용과 n,i서로 저장
}

for (int i = 0; i < n; i++) { //논의수만큼 반복하여
for (int j = 0; j < n; j++) //이중 반복
cin >> graph[i][j]; //논들 사이에서 물을 끌어오는 비용
}

cout << prim(n + 1, n, graph); //수원에서 시작하는 프림 알고리즘
}
92 changes: 92 additions & 0 deletions 1130 - MST/16202.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// Created by 반예원 on 2021/12/07.
//


#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>

using namespace std;
typedef tuple<int, int, int> tp;

vector<int> parent;

//Find 연산
int findParent(int node) {
if (parent[node] < 0) //값이 음수면 루트 정점
return node; //nodq 반환
return parent[node] = findParent(parent[node]); //그래프 압축하며 루트 정점 찾기
}

//Union 연산
bool unionInput(int x, int y) {
int xp = findParent(x); //부모 찾고 선언
int yp = findParent(y);//부모 찾고 선언

if (xp == yp) //같은 집합에 있다면 유니온 할 수 없음
return false; //false로 반환
if (parent[xp] < parent[yp]) { //새로운 루트 xp
parent[xp] += parent[yp]; // 노드 개수갱신
parent[yp] = xp; //다시 xp 를 갱신 하여 부모로
} else { //새로운 루트 yp
parent[yp] += parent[xp]; // 노드 개수갱신
parent[xp] = yp;//다시 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]);// 튜플로부터 값 가져오기
int y = get<2>(edges[i]);// 튜플로부터 값 가져오기

if (unionInput(x, y)) { //유니온 후 true시
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); // 벡터의원소 끝에 값추가
}

bool flag = false; //bool 선언
for (int i = 0; i < k; i++) {
if (flag) { //더이상 mst를 만들 수 없음
cout << 0 << ' '; //0출력
continue;
}
parent.assign(n + 1, -1); //초기화
int ans = kruskal(n, i, edges); //크루스칼 알고리즘 사용
if (ans == 0) //반환 닶이 0이라면
flag = true;//flag값 true로 변경
cout << ans << ' ';
}
}
58 changes: 58 additions & 0 deletions 1130 - MST/1713.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// Created by 반예원 on 2021/12/06.
//
#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; //작다면 추천횟수 갱신
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;

//입력 & 연산
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; //해당 후보의 {추천 횟수, 게시 시간}에 i값 저장
candidate[input].first++; //추천 횟수 증가
}

//출력
for (auto iter = candidate.begin(); iter != candidate.end(); iter++)
cout << iter->first << ' ';
}
52 changes: 52 additions & 0 deletions 1207 - topological sort/2252.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// Created by 반예원 on 2021/12/21.
//

//라이브코딩
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

vector<int> topologicalSort(int n, vector<int> &indegree,vector<vector<int>> &graph){
vector<int> result;
queue<int> q;

for(int i=1;i<=n;i++){
if(!indegree[i])
q.push(i);
}
while(!q.empty()){
int node = q.front();
q.pop();

result.push_back(node);
for(int i=0;i< graph[node].size();i++){
int next_node = graph[node][i];
indegree[next_node]--;
if(!indegree[next_node])
q.push(next_node);
}
}
return result;

}
int main(){
int n,m,a,b;

cin >> n >> m;
vector<int> indegree(n+1,0);
vector<vector<int>> graph(n+1,vector<int>(0));
while(m--){
cin >> a >> b; //a<b
indegree[b]++;
graph[a].push_back(b); // 단방향 그래프 저장
}

vector<int> result = topologicalSort(n,indegree,graph);
for(int i=0;i<n;i++)
cout << result[i] << " ";
return 0;
}


84 changes: 84 additions & 0 deletions 1207 - topological sort/23059.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// Created by 반예원 on 2021/12/21.
//

#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include <unordered_map>

using namespace std;

unordered_map<string, int> indegree;
unordered_map<string, vector<string>> graph;

//위상정렬
vector<string> topologicalSort(set<string> &item) { //위상 정렬
vector<string> result; //결과값 벡터 선언
priority_queue<string, vector<string>, greater<>> pq, pq_temp;
//우선 순위 큐 선언하기
for (auto iter = item.begin(); iter != item.end(); iter++) { //아이템 값 iter로 반복
if (indegree.find(*iter) == indegree.end()) //진입차수가 존재하지 않는다면(0 이라면)
pq.push(*iter); //해당 아이템값 큐에 저장
}
while (!pq.empty()) { //큐가 빌때까지 반복
string node = pq.top(); //해당 윗값 스트링에 저장
pq.pop(); //값 빼기

result.push_back(node); //현재 정점 순서에 삽입
for (int i = 0; i < graph[node].size(); i++) { //그래프 사이즈만큼 반복
string next_node = graph[node][i]; //새로운 스트링 변수에 다음 노드값 저장
indegree[next_node]--; //연결된 정점의 진입차수를 1씩 감소
if (indegree[next_node] == 0) //연결된 정점의 진입차수가 0이 되었다면
pq_temp.push(next_node); //우선 pq_temp에 삽입
}
if (pq.empty() && !pq_temp.empty()) //이전에 진입차수가 0이였던 정점을 모두 탐색했고, 새로 탐색할 정점이 있다면
swap(pq, pq_temp); //해당 정점과, 새로탐색정점 swqp 하기
}
return result; //결과 벡터 반환
}

/**
* 문자열을 인덱스로 사용해야 하기때문에 map을 사용
*
* !주의! 일반 map을 사용하면 시간초과. 따라서 map보다 검색 속도가 빠른 unordered_map 사용해야 함
* -> map은 이진 탐색 트리 형태로 key값을 정렬해서 시간 복잡도가 logN 이지만, unordered_map은 hash형태로 저장해서 시간 복잡도가 1이다
* !주의! 위상 정렬을 할 때 원래는 여러 결과가 가능하지만 해당 문제는 순서배치가 중요 (순서는 예제 3 참고)
* 1. 진입차수가 0인 정점들끼리 정렬해서 사용해야 함
* 2. 진입차수가 0인 정점이 동시에 여러 개일 경우 해당 정점들을 먼저 순서에 배치해야 함.
* -> 따라서 우선순위 큐를 사용하여 진입차수가 0인 정점들을 관리. 이때 동시에 진입차수가 0이 된 정점들을 먼저 배치해야 하므로 우선순위 큐를 2개 사용.
*/

int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
//입출력 속도 향상하기
set<string> item; //존재하는 아이템(정점)
int n; //n값 선언
string a, b; //스트링 a,b값 선언

//입력
cin >> n; //n 값 입력 받기
while (n--) { //n 번 만큼 입력 받기
cin >> a >> b; //a < b
indegree[b]++; //진입차수 증가
graph[a].push_back(b); //단방향 그래프
item.insert(a); //아이템 삽입
item.insert(b); //아이템 삽입
}

//연산
vector<string> result = topologicalSort(item); // item값 위상정렬후 result벡터에 저장

//출력
if (result.size() != item.size()) { // 모든 아이템 출력 못할시
cout << "-1\n"; // -1 출력
return 0;
}
for (int i = 0; i < result.size(); i++) //결과 사이즈 만큼 반복
cout << result[i] << '\n'; //결과 값 출력

return 0;
}
Loading