충남대학교 컴퓨터공학과 이영석 교수님의 "알고리즘" 강의 실습 내용입니다.

  • 실습 문제들의 코드 위주로 구성되어 있습니다.

문제 1: Dijkstra Algorithm 구현

#include <functional>
#include <iostream>
#include <vector>
#include <array>
#include <queue>
#include <map>
 
using namespace std;
 
#define TOOFAST ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
#define INTMAX 2147483647
#define COST 0
#define NODE 1
 
struct Info {
	string node;
	int cost;
	Info(): node(""), cost(INTMAX) {}
	Info(string n, int c): node(n), cost(c) {}
	bool operator<(Info t) const { return this->cost < t.cost; }
	bool operator>(Info t) const { return this->cost > t.cost; }
	bool operator==(Info t) const { return this->cost == t.cost; }
};
 
int main() {
	TOOFAST;
 
	int V, E;
	cin >> V >> E;
 
	string start, end;
	cin >> start >> end;
 
	// 각 노드에 대해 바로 이전에 방문한 노드와 해당 노드에 도달하기까지의 최소비용을 저장하기 위함
	map<string, Info> D;
	// 각 노드와 연결된 노드 및 비용을 저장하기 위함
	map<string, vector<Info>> conn;
	// 방문한 노드를 체크하기 위함
	map<string, bool> selected;
	// 존재하는 노드를 입력받으며 각 자료구조를 초기화함
	for(int i = 0; i < V; i++) {
		string v;
		cin >> v;
		D[v] = Info(start, INTMAX);
		conn[v] = {};
		selected[v] = false;
	}
 
	// 양방향 연결관계를 생성해줌
	for(int i = 0; i < E; i++) {
		string u, v;
		int w;
		cin >> u >> v >> w;
		conn[u].push_back(Info(v, w));
		conn[v].push_back(Info(u, w));
	}
 
	// 시작노드의 비용을 0으로 바꿔줌
	D[start].cost = 0;
 
	// 주어진 시작노드에서 출발하도록 함
	priority_queue<Info, vector<Info>, greater<Info>> pq;
	pq.push(Info(start, 0));
 
	while(!pq.empty()) {
 
		// 현재 이동비용이 제일 적은 경로를 선택
		auto h = pq.top();
		pq.pop();
 
		// 선택된 노드를 방문함으로 변경
		selected[h.node] = true;
 
		for(const auto& next : conn[h.node]) {
 
			// 현재 노드와 연결된 노드중 이미 방문한 노드는 무시함
			if(selected[next.node]) {
				continue;
			}
 
			// 시작지점에서 현재 노드와 연결된 노드까지 가는 비용이 현재 노드를 거쳐서 가는 비용보다 클 경우
			// 해당 노드의 최소비용을 현재 노드를 거칠때의 비용으로 수정하고
			// 바로 이전에 방문한 노드를 현재 노드로 지정해주며
			// 우선순위 큐에 넣어줌
			if(D[next.node].cost > D[h.node].cost + next.cost) {
				D[next.node].cost = D[h.node].cost + next.cost;
				D[next.node].node = h.node;
				pq.push(Info(next.node, D[next.node].cost));
			}
		}
	}
 
	// 종점노드까지의 이동경로를 알아내기 위해 바로 이전에 방문한 노드를 추적함
	vector<string> path = { end };
	string it = end;
	while(it != start) {
		path.push_back(D[it].node);
		it = D[it].node;
	}
 
	// 결과 출력
	for(auto rit = path.rbegin(); rit < path.rend(); rit++) {
		cout << *rit;
	}
	cout << '\n';
	cout << D[end].cost << '\n';
 
	return 0;
}

문제 2: Bellman-Ford Algorithm 구현

#include <iostream>
#include <vector>
#include <array>
#include <map>
 
using namespace std;
 
#define TOOFAST ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
#define INTMAX 2147483647
 
struct Edge {
	string first, second;
	int cost;
};
 
int main() {
	TOOFAST;
 
	int V, E;
	cin >> V >> E;
 
	string start, end;
	cin >> start >> end;
 
	// 바로 이전에 거쳤던 간선과 해당 노드에 가기까지의 최소비용을 저장하기 위함
	map<string, pair<Edge, int>> D;
	// 노드 정보를 입력받으며 자료구조를 초기화함
	for(int i = 0; i < V; i++) {
		string v;
		cin >> v;
		D[v] = make_pair(Edge{start, start, INTMAX}, INTMAX);
	}
	D[start].second = 0;
 
	// 단방향 간선의 정보를 입력받음
	vector<Edge> edges;
	edges.reserve(2 * E);
	for(int i = 0; i < E; i++) {
		string first, second;
		int cost;
		cin >> first >> second >> cost;
		edges.push_back(Edge{first, second, cost});
	}
 
	// 모든 노드에 대해
	for(int i = 0; i < V - 1; i++) {
		// 모든 간선을 조사하며
		for(const auto& edge : edges) {
			// 간선의 시작점까지의 최소비용이 무한대가 아니고 간선의 도착점까지의 최소비용이
			// 간선의 시작점까지의 최소비용과 간선의 비용을 합한 것보다 클때
			// 합한 값으로 최소비용을 수정함
			if(D[edge.first].second != INTMAX && D[edge.second].second > D[edge.first].second + edge.cost) {
				D[edge.second].second = D[edge.first].second + edge.cost;
				D[edge.second].first = edge;
			}
		}
	}
 
	// 모든 간선에 대해 한번 더 동일한 과정을 거쳤을때 변화가 생긴다면 최소비용이 음의 무한대인 경로가 존재하므로
	// 최소비용을 알 수 없음을 출력함
	for(const auto& edge : edges) {
		if(D[edge.first].second != INTMAX && D[edge.second].second > D[edge.first].second + edge.cost) {
			cout << "Negative\n";
			return 0;
		}
	}
 
	// 바로 이전에 방문한 노드를 추적하며 목적지까지 가는 최소비용 경로를 알아냄
	vector<Edge> path = { D[end].first };
	string it = end;
	while(it != start) {
		path.push_back(D[it].first);
		it = D[it].first.first;
	}
 
	// 결과를 출력함
	for(auto rit = path.rbegin(); rit < path.rend() - 1; rit++) {
		cout << rit->first << ' ' << rit->second << ' ' << (rit->cost) << '\n';
	}
 
	return 0;
}

문제 3: 호캉스

#include <iostream>
#include <map>
#include <vector>
 
using namespace std;
 
#define TOOFAST ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
#define INTMAX 2147483647
 
struct Edge{
	string src, dst;
	int cost;
};
 
int main() {
	TOOFAST;
 
	int V, E, L;
	cin >> V >> E >> L;
 
	vector<string> nodes(V);
	for(int i = 0; i < V; i++) {
		cin >> nodes[i];
	}
 
	// 한 노드에서 다른 노드로 가는 비용을 초기화함
	map<string, map<string, int>> D;
	for(const auto& s : nodes) {
		for(const auto& d : nodes) {
			if(s == d) {
				D[s][d] = 0;
			} else {
				D[s][d] = INTMAX;
			}
		}
	}
 
	// 간선의 정보를 입력받아 양방향 연결관계를 맺어줌
	for(int i = 0; i < E; i++) {
		string src, dst;
		int cost;
		cin >> src >> dst >> cost;
		D[src][dst] = cost;
		D[dst][src] = cost;
	}
 
	for(const auto& k : nodes) {
		for(const auto& i : nodes) {
			for(const auto& j : nodes) {
				// 노드 i에서 j로 가는 비용이 노드 k를 거쳐 가는 것이 더 비용이 적다면 최소비용을 수정함
				if((D[i][k] != INTMAX) && (D[k][j] != INTMAX)) {
					D[i][j] = min(D[i][j], D[i][k] + D[k][j]);
				}
			}
		}
	}
 
	string resNode = "";
	int resNum = -1;
	for(const auto& v : nodes) {
		int cnt = 0;
		// 각 노드에 대해 연결된 노드들과의 비용이 주어진 한계보다 작은 노드들의 갯수를 셈
		for(const auto& n : nodes) {
			if(D[v][n] <= L) {
				cnt++;
			}
		}
		// 노드 갯수의 최대값을 수정함
		if(cnt > resNum) {
			resNum = cnt;
			resNode = v;
		}
	}
 
	// 출력
	cout << resNode << ' ' << resNum << '\n';
 
	return 0;
}