그래프 표현 방법(코드 중심)으로 설명합니다.
다익스트라 알고리즘을 설명하고, 코드를 구현합니다.
(옵션) 백준 알고리즘 다음 문제를 풀어봅니다.
1753 최단경로
1238 파티
1504 특정 최단 경로
1177 최소비용 구하기
그래프를 표현하는 방법은 대표적으로 대표적인 인접행렬(adjacency matrix), 인접 리스트(adjacency list)로 나눌 수 있다. 그래프의 표현방법에 따라 그래프에 수행시키려는 연산과 이용하려는 응용의 차이를 가진다.
하나의 정점에서 간선에 의해 직접 연결된 정점을 나타내는 인접 정점(adjacent vertex)을 정점의 개수가 n인 가로 세로 n x n 의 행렬M에 표현한다. 인접 정점 간의 간선이 존재할 때와 존재하지 않은 경우를 행렬M의 값으로 표현한다.
2차원 배열의 형태로 그래프를 표현
- 정점 i와 정점 j의 간선 존재O
M[i][j] = 1- 정점 i와 정점 j의 간선 존재X
M[i][j] = 0- 대각행렬은 모두 0
M[i][i] = 0 , M[j][j] = 0
int size; //정점의 개수
char vertices[]; //정점의 값
int adj[][]; //인접행렬의 정보
AdjMatGraph() //생성자
bool isEmpty() //인접행렬의 간선 유무
bool isFull()
//정점
char insertVertex() //정점삽입
int deleteVertex()
//간선
int insertEdge( u, v) //간선삽입(무방향 그래프)
int deleteEdge( u, v)
int adjacent( v): int[]
//그래프 정보출력
display()
그래프 구현을 위해 위와 같이 변수와 메소드를 구성해준다.
#define MAX_VTXS 100
class AdjMatGraph {
protected:
int size;
char vertices[MAX_VTXS];
int adj[MAX_VTXS][MAX_VTXS];
public:
AdjMatGraph( ) { reset(); }
char getVertex(int i) { return vertices[i]; }
int getEdge(int i, int j) { return adj[i][j]; }
void setEdge(int i, int j, int val) { adj[i][j] = val; }
bool isEmpty(){ return size==0; }
bool isFull(){ return size>=MAX_VTXS; }
// 그래프 초기화 ==> 공백 상태의 그래프
void reset() {
size=0;
for(int i=0 ; i<MAX_VTXS ; i++ )
for(int j=0 ; j<MAX_VTXS ; j++ )
setEdge(i,j,0);
}
// 정점 삽입
void insertVertex( char name ) {
if( !isFull() ) vertices[size++] = name;
else printf("Error: 그래프 정점 개수 초과\n");
}
// 간선 삽입: 무방향 그래프의 경우임.
void insertEdge( int u, int v ) {
setEdge(u,v,1);
setEdge(v,u,1); }
// 그래프 정보 출력 (화면이나 파일에 출력)
void display( FILE *fp= stdout) {
fprintf(fp, "%d\n", size);// 정점의 개수 출력
for( int i=0 ; i<size ; i++ ) {// 각 행의 정보 출력
fprintf(fp,"%c ", getVertex(i));// 정점의 이름 출력
for( int j=0 ; j<size ; j++ )// 간선 정보 출력
fprintf(fp, " %3d", getEdge(i,j));
fprintf(fp,"\n");
}
}
};
모든 정점을 연결 리스트에 표현한다. 각 정정에 인접한 정점들을 리스트로 구현한다.
간선을 포인터 변수를 이용해서 나타낸다. Node 클래스와 AdjListGraph 클래스로 구현해 상속을 통해 두 클래스를 사용한다.
Node 클래스
id: int //node의 값
link: Node* //다음노드를 가르키는 포인터
Node(int id) //생성자
getLink(): Node* //다음 노드의 포인터
setLink(Node* n)
display()
AdjListGraph 클래스
size: int //정점의 개수
vertices: char[] //정점의 정보
adj: Node*[] //각 정점의 인접리스트
AdjListGraph () //생성자
isEmpty(): bool
isFull(): bool
//정점 추가 및 삭제
insertVertex(char v)
deleteVertex(int v)
//간선 추가 및 삭제
insertEdge(int u, int v)
deleteEdge(int u, int v)
adjacent(int v): Node*
display()
// 인접 리스트를 이용한 그래프: 노드 클래스
#define MAX_VTXS 100
class Node {
protected:
int id; // 정점의 id
Node* link; // 다음 노드의 포인터
public:
Node(int i, Node *l=NULL) : id(i), link(l) { }
~Node() {
if( link != NULL ) delete link;
}
int getId() { return id; }
Node* getLink() { return link; }
void setLink(Node* l) { link = l; }
};
// 연결 리스트를 위한 노드 그래프 클래스 포함
class AdjListGraph {
protected:
int size; // 정점의 개수
char vertices[MAX_VTXS]; // 정점 정보
Node* adj[MAX_VTXS]; // 각 정점의 인접 리스트
public:
AdjListGraph() : size(0) { }
~AdjListGraph(){ reset(); }
void reset(void) {
for( int i=0 ; i<size ; i++ )
if( adj[i] != NULL ) delete adj[i];
size = 0;
}
void insertVertex( char val ) { // 정점 삽입 연산
if( !isFull() ) {
vertices[size] = val;
adj[size++] = NULL;
}
else printf("Error: 그래프 정점 개수 초과\n");
}
void insertEdge( int u, int v) { // 간선 삽입 연산
adj[u] = new Node (v, adj[u]); // 인접 리스트에 추가
adj[v] = new Node (u, adj[v]); // 방향 그래프 ==> 주석 처리함
}
void display( ) {
printf("%d\n", size); // 정점의 개수 출력
for( int i=0 ; i<size ; i++ ) { // 각 행의 정보 출력
printf("%c ", getVertex(i)); // 정점의 이름 출력
for( Node *v=adj[i] ; v != NULL ; v=v->getLink() )
printf(" %c", getVertex(v->getId()) );
printf("\n");
}
}
Node* adjacent(int v) { return adj[v]; }
void insertEdge( int u, int v) { // 간선 삽입 연산
adj[u] = new Node (v, adj[u]); // 인접 리스트에 추가
adj[v] = new Node (u, adj[v]); // 방향 그래프 ==> 주석 처리함
}
void display( ) {
printf("%d\n", size); // 정점의 개수 출력
for( int i=0 ; i<size ; i++ ) { // 각 행의 정보 출력
printf("%c ", getVertex(i)); // 정점의 이름 출력
for( Node *v=adj[i]; v != NULL; v=v->getLink() )
printf(" %c", getVertex(v->getId()) );
printf("\n");
}
}
Node* adjacent(int v) { return adj[v]; }
bool isEmpty() { return size ==0; }
bool isFull() { return size >= MAX_VTXS; }
char getVertex( int i ) {return vertices[i]; }
}; // end of class AdjListGraph
• 시작 정점 v에서 다른 모든 정점까지의 최단 경로를 찾는 알고리즘
▪ 시작 정점 v
▪ 집합 S : v에서부터의 최단경로를 이미 찾은 정점들의 집합
▪ dist[u] 배열: S에 있는 정점들 만을 거쳐서 가는 v-u 간 경로의 길이
▪ 초기값
shortest_path_dijkstra(v)
S <- {v}
for 각 정점 w in G do
dist[w] = weight[v][w]
while 모든 정점이 S에 포함되지 않으면 do
u <- 집합 S에 속하지 않는 정점 중에서 최소 dist를 갖는 정점 선택
S <- S union {u}
for u에 인접하고 S에 있지 않은 모든 정점 w do
if ( dist[u] + weight[u][w] < dist[w] )
then dist[w] dist[u] + weight[u][w]
// Dijkstra알고리즘의 최단 경로 탐색 기능이 추가된 그래프
class WGraphDijkstra : public WGraph {
int dist[MAX_VTXS]; // 시작노드로부터의 최단경로 거리
bool found[MAX_VTXS]; // 방문한 정점 표시 → 집합 S
public:
int chooseVertex() { // 가장 비용 적은 미방문 정점을 반환
int min = INF;
int minpos = -1;
for( int i=0 ; i<size ; i++ )
if( dist[i]< min && !found[i] ){
min = dist[i];
minpos = i;
}
return minpos;
}
void printDistance() { //모든 정점들의 dist[] 값 출력
for( int i=0 ; i<size ; i++)
printf("%5d", dist[i]);
printf("\n");
}
// Dijkstra의 최단 경로 알고리즘: start 정점에서 시작함.
void ShortestPath( int start ) {
for( int i=0 ; i<size ; i++) { //초기화: dist[], found[]
dist[i] = getEdge(start,i); //인접행렬 값 반환(간선 가중치)
found[i] = false; //처음에 S집합은 비어있음.
}
found[start] = true; // S에 포함
dist[start] = 0; // 최초 거리
for( int i=0 ; i<size ; i++ ){
printf("Step%2d:", i+1);
printDistance(); // 모든 dist[] 배열값 출력
int u = chooseVertex(); // S에 속하지 않은 비용 가장 작은 정점 반환
found[u] = true; // 집합 S에 포함
for( int w=0 ; w<size ; w++) {
if( found[w] == false )//S에 속하지 않는 노드 w의 dist값 갱신
if( dist[u] + getEdge(u,w) < dist[w] )
dist[w] = dist[u] + getEdge(u,w);
} // u를 거쳐가는 것이 최단 거리이면 dist[] 갱신
}
}
};
주어진 시작점에서 다른 모든 정점으로의 최단 경로를 구해야 한다. 최단 경로의 경로값, 즉 최소비용을 구하도록 한다. 다익스트라 알고리즘을 이용한다.
(배열만으로 구현하는 방법과 우선순위큐를 이용하는 방법으로 나뉜다.
배열으로만 구현하는 방법은 메모리 초과가 발생한다!(포스팅 참고))
int V; //정점의 개수
int E; //간선의 개수
int start; //시작정점의 번호
int dist[MAX]; //가중치
vector<pair<int, int>> vertex[MAX]; //출발정점과 도착정점을 pair로 묶는다.
void input() //입력값 가져오기
{
cin >> V >> E >> Start;
for (int i = 0; i < E; i++)
{
int a, b, c;
cin >> a >> b >> c;
Vertex[a].push_back(make_pair(b, c));
}
for (int i = 1; i <= V; i++) Dist[i] = INF;
}
void Solution() //우선순위큐를 이용한다.
{
priority_queue<pair<int, int>> PQ;
PQ.push(make_pair(0, Start));
Dist[Start] = 0;
while (PQ.empty() == 0)
{
int Cost = -PQ.top().first;
int Cur = PQ.top().second;
PQ.pop();
for (int i = 0; i < Vertex[Cur].size(); i++)
{
int Next = Vertex[Cur][i].first;
int nCost = Vertex[Cur][i].second;
if (Dist[Next] > Cost + nCost)
{
Dist[Next] = Cost + nCost;
PQ.push(make_pair(-Dist[Next], Next));
}
}
}
for (int i = 1; i <= V; i++)
{
if (Dist[i] == INF) cout << "INF" << endl;
else cout << Dist[i] << endl;
}
}
void Solve()
{
Input();
Solution();
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
//freopen("Input.txt", "r", stdin);
Solve();
return 0;
}
단순한 다익스트라 문제와 달리 최단거리로 목적지, 즉 도착정점까지 이동해 다시 출발 지점으로 되돌아오는 최단거리를 구해야 한다.
정점들의 정보를 입력 받을 때 역방향의 간선을 따로 입력 받아 최단거리를 구할 수 있다.
#define pii pair<int, int>
int N; //마을의 수(학생의 수)
int M; //단방향 도로의 개수
int X; //파티을 위해 모이는 마을의 번호
const int INF = 1e9+7;
vector<pii> graph[1001]; //pair를 적용한다.
vector<int> dist;
int resdist[1001]; //가중치 반영
void input(){ //입력값 받아오기
int u, v, t; //첫줄 입력값
cin >> N >> M >> X;
for(int i = 0; i < M; i++){
cin >> u >> v >> t;
graph[u].push_back(make_pair(t, v)); //pair적용해서 간선의 출발정점과 도착정점을 표현한다.
}
}
void Dijstra(int S){ //다익스트라 알고리즘
dist.clear(); //가중치 비운다.
dist.resize(N+1, INF);
dist[S] = 0;
priority_queue<pii, vector<pii >, greater<pii > > que;
que.push({0, S});
while(!que.empty()){
int min_cost = que.top().first;
int now = que.top().second;
que.pop();
if(min_cost > dist[now]) continue;
for(int i = 0; i < graph[now].size(); i++){
int next = graph[now][i].second;
int next_cost = min_cost + graph[now][i].first;
if(next_cost < dist[next]){
dist[next] = next_cost;
que.push({next_cost, next});
}
}
}
}
int main(){
ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
input();
for(int i = 1; i <= N; i++){
Dijstra(i);
// i가 X로 가는 최단거리 half
resdist[i] = dist[X];
}
Dijstra(X);
int res = 0;
for(int i = 1; i <= N; i++){
resdist[i] += dist[i];
res = max(res, resdist[i]);
}
cout << res;
return 0;
}
해당 링크를 통해서 BFS 적용 문제를 확인할 수 있다.
https://velog.io/@ehekaanldk/BFS-%EB%AC%B8%EC%A0%9C-%EC%98%88%EC%A0%9C