BFS와 DFS를 공부해 볼 수 있는 간단한 문제를 풀어보자.


환경 및 선수조건


문제

문제 링크

  • 문제: <그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집들의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여기서 연결되었다는 것은 어떤 집이 좌우, 혹은 아래위로 다른 집이 있는 경우를 말한다. 대각선상에 집이 있는 경우는 연결된 것이 아니다. <그림 2>는 <그림 1>을 단지별로 번호를 붙인 것이다. 지도를 입력하여 단지수를 출력하고, 각 단지에 속하는 집의 수를 오름차순으로 정렬하여 출력하는 프로그램을 작성하시오.


접근법

  • 지도에서 하나하나 조사하면서 집을 발견하면 단지로 정의하고 집의 수를 세는 방법으로 풀이가 가능합니다.
  • 하나하나 조사를 해야하기 때문에 완전탐색으로 생각할 수 있습니다.
  • 단지 안에서 집의 수를 세는건 주변으로 뻗어 나가는 방법을 사용하기 때문에 BFS나 DFS를 통해서 4방향으로 탐색을 진행하면 됨을 알 수 있습니다.


풀이법

  1. 전체의 지도를 돌면서 집이 있는지 확인하다.
  2. 집(배열의 값 == 1)을 발견하면 단지로 정의하고 ID를 부여한다.
  3. BFS 또는 DFS 탐색을 통해서 해당 단지의 집의 수를 구한다.
  4. 해당 단지의 집의 수를 다 구하게 되면 해당 집의 ID를 인덱스로 하는 배열에 추가해준다.
  5. 전체 지도의 탐색이 다 끝나면 배열을 오름차순으로 정렬하고 출력한다.


완전탐색이란?

정의

  • 완전탐색(Bruete Force): 가능한 모든 경우의 수를 다 시도해 답을 찾는 방법
  • 정확하고 확실하게 답을 찾을 수 있다는 장점이 있지만 시간이 오래 걸린다는 단점이 있다.
  • 예시: 1부터 n까지의 수를 모두 더하기, 난수에서 배열에서 특정한 수 찾기 등등…


BFS와 DFS

개념

  • DFS(깊이우선탐색): 현재 정점에서 갈 수 있는 점들까지 들어가면서 탐색
  • BFS(너비우선탐색): 현재 정점에 연결된 가까운 점들부터 탐색
  • 위의 이미지를 참고하시면 비교가 쉽습니다. 해당 번호 순서대로 탐색을 합니다.

방법

  • DFS: 스택 또는 재귀함수로 구현
  • BFS: 큐를 이용해서 구현


풀이코드

DFS(Recursion)

  • 재귀함수를 이용한 DFS 풀이
// DFS - Recursion
void dfs_recursion(int x, int y) {

    // 함수에 들어왔으면 -> 방문으로 표시
    visited[x][y] = true;
    // 해당 단지의 집의 수를 증가시킴
    groups[group_id]++;

    // 해당 위치의 주변을 확인
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];

        // 지도를 벗어나지 않고
        if(0 <= nx && nx < n && 0 <= ny && ny < n){
            // 집이면서 방문하지 않았다면 -> 방문
            if(map[nx][ny] == 1 && visited[nx][ny] == false){
                dfs_recursion(nx, ny);
            }
        }
    }
}

DFS(Stack)

  • 스택을 이용한 DFS 풀이
// DFS - Stack
void dfs_stack(int x, int y) {

    stack< pair<int,int> > s; // 이용할 스택, (x,y) -> (행, 열)
    s.push(make_pair(x,y)); // pair를 만들어서 stack에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;

    while(!s.empty()){

        // 스택의 top 원소 꺼내기
        x = s.top().first;
        y = s.top().second;
        s.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    s.push(make_pair(x,y)); // push하는 경우이기 때문에 현재 위치도 넣어준다.
                    s.push(make_pair(nx,ny)); // 스택에 현재 nx,ny도 푸시
                }
            }
        }   
    }
}

BFS(Queue)

  • 큐를 이용한 DFS 풀이
// BFS
void bfs(int x, int y){

    queue< pair<int,int> > q; // 이용할 큐, (x,y) -> (행, 열)
    q.push(make_pair(x,y)); // pair를 만들어서 queue에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;  

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    // 큐에 현재 nx,ny를 추가
                    q.push(make_pair(nx,ny));   
                }
            }
        }
    }
}

전체 코드

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
#include <algorithm>

#define MAX_SIZE 25

using namespace std;

// 위, 오른, 아래, 왼
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};

int n; // 행과 열의 수
int group_id; // 단지의 번호로 첫번째 단지부터 1로 시작
int groups[MAX_SIZE * MAX_SIZE]; // 각 단지별 집의 수

int map[MAX_SIZE][MAX_SIZE]; // 지도
bool visited[MAX_SIZE][MAX_SIZE]; // 방문했는지를 표시하는 지도

// DFS - Recursion
void dfs_recursion(int x, int y) {

    // 함수에 들어왔으면 -> 방문으로 표시
    visited[x][y] = true;
    // 해당 단지의 집의 수를 증가시킴
    groups[group_id]++;

    // 해당 위치의 주변을 확인
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];

        // 지도를 벗어나지 않고
        if(0 <= nx && nx < n && 0 <= ny && ny < n){
            // 집이면서 방문하지 않았다면 -> 방문
            if(map[nx][ny] == 1 && visited[nx][ny] == false){
                dfs_recursion(nx, ny);
            }
        }
    }
}

// DFS - Stack
void dfs_stack(int x, int y) {

    stack< pair<int,int> > s; // 이용할 스택, (x,y) -> (행, 열)
    s.push(make_pair(x,y)); // pair를 만들어서 stack에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;

    while(!s.empty()){

        // 스택의 top 원소 꺼내기
        x = s.top().first;
        y = s.top().second;
        s.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    s.push(make_pair(x,y)); // push하는 경우이기 때문에 현재 위치도 넣어준다.
                    s.push(make_pair(nx,ny)); // 스택에 현재 nx,ny도 푸시
                }
            }
        }   
    }
}

// BFS
void bfs(int x, int y){

    queue< pair<int,int> > q; // 이용할 큐, (x,y) -> (행, 열)
    q.push(make_pair(x,y)); // pair를 만들어서 queue에 넣습니다.

    // 처음 x,y를 방문 했기때문에
    visited[x][y] = true;
    groups[group_id]++;  

    while(!q.empty()){

        // 큐의 현재 원소를 꺼내기
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // 해당 위치의 주변을 확인
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // 지도를 벗어나지 않고
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // 집이면서 방문하지 않았다면 -> 방문
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // 해당 단지의 집의 수를 증가시킴
                    groups[group_id]++;

                    // 큐에 현재 nx,ny를 추가
                    q.push(make_pair(nx,ny));   
                }
            }
        }
    }
}

int main (){

    scanf("%d", &n);

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++)
            //입력을 1개씩 숫자로 끊어서 받습니다 -> %1d
            scanf("%1d", &map[i][j]);
    }

    // 전체 지도 탐색
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            // 집이면서 방문하지 않았다면 -> 방문
            if(map[i][j]==1 && visited[i][j]==false){

                // 해당 지역에 단지 id를 부여하고
                group_id++;

                // 탐색
                //dfs_recursion(i, j);
                //dfs_stack(i, j);
                //bfs(i, j);
            }
        }
    }

    // 단지마다 집들의 수로 오름차순 정렬
    // ID는 1부터 시작
    // 함수 사용법: https://twpower.github.io/71-use-sort-and-stable_sort-in-cpp
    sort(groups + 1, groups + group_id + 1);

    printf("%d\n", group_id);
    for (int i = 1; i <= group_id; i++) {
        printf("%d\n", groups[i]);
    }
}


참고자료

Solve basic brute force problem which use dfs and bfs.


Environment and Prerequisite

  • C++
  • Basic concept of BFS and DFS


Problem

  • Problem: There is a map like picture above. 1 represents house, 0 represents nothing. Chulsoo plans to define connected houses as apartment block and assign same number to each block. Connected houses means that specific house is connected to other houses near up, right, down and left(like on the image). Houses in cross line are not connected. Picture on the right is numbered apartment block of picture on left. Find each apartment blocks number of houses and print those houses in ascending order.


Apporach

  • We can find apartment blocks by scanning all places on the map. While searching all places on map, define apartment block when we meet 1(house) and count number of houses.
  • We need to scan all places so it is brute force search.
  • In apartment block, we need to spread out from one position to around neighbors. So we can think it as BFS or DFS.


Solution

  1. Check if there is a 1(house), while scanning all places on map.
  2. If there is a 1(house), define it as new apartment block and assign ID to it.
  3. Find number of houses in apartment block by using BFS or DFS.
  4. Add number of houses in apartment block to array.
  5. After scanning all places on map, sort array in ascending order and print it.


Definition

  • Bruete Force or Exhaustive Search: Solution which search all the possible cases.
  • It is accurate but takes long time.
  • Ex: Sum from 1 to n, Find specific number in random array


BFS and DFS

Concept

  • DFS(Depth First Search): Explores as far as possible along each branch before backtracking(move to neighbor).
  • BFS(Breadth First Search): Explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.
  • See above attached image. The number is traverse order.

Method

  • DFS: Use stack or recursion
  • BFS: Use queue


Solution Codes

DFS(Recursion)

  • DFS solution using recursion
// DFS - Recursion
void dfs_recursion(int x, int y) {

    // step into function -> it means visited
    visited[x][y] = true;
    // increase number of houses in group_id apartment block
    groups[group_id]++;

    // search around current x,y position
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];

        // if nx and ny are in map
        if(0 <= nx && nx < n && 0 <= ny && ny < n){
            // if cur nx,ny is not visited then -> visit
            if(map[nx][ny] == 1 && visited[nx][ny] == false){
                dfs_recursion(nx, ny);
            }
        }
    }
}

DFS(Stack)

  • DFS solution using stack
// DFS - Stack
void dfs_stack(int x, int y) {

    stack< pair<int,int> > s; // stack for dfs, (x,y) -> (row, col)
    s.push(make_pair(x,y)); // make pair data structure and push to stack

    // first visit to x,y -> make x,y position visited
    visited[x][y] = true;
    // increase number of houses in group_id apartment block
    groups[group_id]++;

    while(!s.empty()){

        // get top element of stack
        x = s.top().first;
        y = s.top().second;
        s.pop();

        // search around current x,y position
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // if nx and ny are in map
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // if cur nx,ny is not visited then -> visit
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // increase number of houses in group_id apartment block
                    groups[group_id]++;

                    // also push current x,y and nx,ny
                    // because it is dfs(search around other positions after processing nx,ny then come back to x,y)
                    s.push(make_pair(x,y));
                    s.push(make_pair(nx,ny));
                }
            }
        }   
    }
}

BFS(Queue)

  • BFS solution using queue
// BFS
void bfs(int x, int y){

    queue< pair<int,int> > q; // queue for dfs, (x,y) -> (row, col)
    q.push(make_pair(x,y)); // make pair data structure and push to queue

    // first visit to x,y -> make x,y position visited
    visited[x][y] = true;
    // increase number of houses in group_id apartment block
    groups[group_id]++;  

    while(!q.empty()){

        // get front element of queue
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // search around current x,y position
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // if nx and ny are in map
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // if cur nx,ny is not visited then -> visit
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // increase number of houses in group_id apartment block
                    groups[group_id]++;

                    // push current nx,ny to queue
                    q.push(make_pair(nx,ny));   
                }
            }
        }
    }
}

Full Code

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
#include <algorithm>

#define MAX_SIZE 25

using namespace std;

// up, right, down, left
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};

int n; // number of rows and columns
int group_id; // apartment block id, it starts from 1
int groups[MAX_SIZE * MAX_SIZE]; // number of houses in each apartment block

int map[MAX_SIZE][MAX_SIZE]; // map
bool visited[MAX_SIZE][MAX_SIZE]; // map for visited or not

// DFS - Recursion
void dfs_recursion(int x, int y) {

    // step into function -> it means visited
    visited[x][y] = true;
    // increase number of houses in group_id apartment block
    groups[group_id]++;

    // search around current x,y position
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];

        // if nx and ny are in map
        if(0 <= nx && nx < n && 0 <= ny && ny < n){
            // if cur nx,ny is not visited then -> visit
            if(map[nx][ny] == 1 && visited[nx][ny] == false){
                dfs_recursion(nx, ny);
            }
        }
    }
}

// DFS - Stack
void dfs_stack(int x, int y) {

    stack< pair<int,int> > s; // stack for dfs, (x,y) -> (row, col)
    s.push(make_pair(x,y)); // make pair data structure and push to stack

    // first visit to x,y -> make x,y position visited
    visited[x][y] = true;
    // increase number of houses in group_id apartment block
    groups[group_id]++;

    while(!s.empty()){

        // get top element of stack
        x = s.top().first;
        y = s.top().second;
        s.pop();

        // search around current x,y position
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // if nx and ny are in map
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // if cur nx,ny is not visited then -> visit
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // increase number of houses in group_id apartment block
                    groups[group_id]++;

                    // also push current x,y and nx,ny
                    // because it is dfs(search around other positions after processing nx,ny then come back to x,y)
                    s.push(make_pair(x,y));
                    s.push(make_pair(nx,ny));
                }
            }
        }   
    }
}

// BFS
void bfs(int x, int y){

    queue< pair<int,int> > q; // queue for dfs, (x,y) -> (row, col)
    q.push(make_pair(x,y)); // make pair data structure and push to queue

    // first visit to x,y -> make x,y position visited
    visited[x][y] = true;
    // increase number of houses in group_id apartment block
    groups[group_id]++;  

    while(!q.empty()){

        // get front element of queue
        x = q.front().first;
        y = q.front().second;
        q.pop();

        // search around current x,y position
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            // if nx and ny are in map
            if(0 <= nx && nx < n && 0 <= ny && ny < n){
                // if cur nx,ny is not visited then -> visit
                if(map[nx][ny] == 1 && visited[nx][ny] == false){
                    visited[nx][ny]=true;

                    // increase number of houses in group_id apartment block
                    groups[group_id]++;

                    // push current nx,ny to queue
                    q.push(make_pair(nx,ny));   
                }
            }
        }
    }
}

int main (){

    scanf("%d", &n);

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++)
            // get integer one by one -> %1d
            scanf("%1d", &map[i][j]);
    }

    // search all values in map
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            // if cur i,j is not visited then -> visit
            if(map[i][j]==1 && visited[i][j]==false){

                // assign id to current position(apartment block)
                group_id++;

                // Search
                //dfs_recursion(i, j);
                //dfs_stack(i, j);
                //bfs(i, j);
            }
        }
    }

    // sort number of houses in each apartment block in ascending order
    // ID starts from 1
    // usage: https://twpower.github.io/71-use-sort-and-stable_sort-in-cpp
    sort(groups + 1, groups + group_id + 1);

    printf("%d\n", group_id);
    for (int i = 1; i <= group_id; i++) {
        printf("%d\n", groups[i]);
    }
}


Reference

VirtualBox 가상머신을 백그라운드에서 실행해보자


환경 및 선수조건

  • Linux or Mac
  • VirtualBox and VM(미리 만들어진)


VBoxManage 명령어

VBoxManage 명령어란?

  • VBoxManage: VBoxManage는 Oracle VirtualBox를 사용할 수 있는 커맨드라인 명령어입니다.
  • VirtualBox를 API를 통해 사용할 수 있는 명령어라고 볼 수 있습니다.
  • API DOC: https://www.virtualbox.org/manual/ch08.html


VM을 백그라운드에서 실행

  • 기본
VBoxManage startvm [VM NAME] --type headless
  • 예제
VBoxManage startvm "Ubuntu 14.04" --type headless


VM을 현재 상태로 저장

  • 기본
VBoxManage controlvm [VM NAME] savestate
  • 예제
VBoxManage controlvm "Ubuntu 14.04" savestate


참고자료

Run VirtualBox VM in background by using command line interface


Environment and Prerequisite

  • Linux or Mac
  • VirtualBox and VM(alreaday amde)


VBoxManage Command

What is VBoxManage command?


Run VM in background

  • Usage
VBoxManage startvm [VM NAME] --type headless
  • Example
VBoxManage startvm "Ubuntu 14.04" --type headless


Save VM in current state

  • Usage
VBoxManage controlvm [VM NAME] savestate
  • Usage
VBoxManage controlvm "Ubuntu 14.04" savestate


Reference

각 언어에서 난수를 생성하는 방법을 알아보자


환경

  • C, C++, Python, Javascript, Java, Shell Script


난수 생성 코드

C

  • rand 함수 이용
  • rand(): 0과 RAND_MAX 사이에 난수 정수 값을 반환
  • srandtime함수는 랜덤 생성할 시드 값을 위해 사용
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main (){

    // use time for seed
    srand(time(NULL));

    // print one number in range 1 ~ 50
    printf("%d\n", rand() % 50 + 1);

    return 0;
}


C++

  • rand 함수 이용
  • rand(): 0과 RAND_MAX 사이에 난수 정수 값을 반환
  • srandtime함수는 랜덤 생성할 시드 값을 위해 사용
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

int main (){

    // use time for seed
    srand(time(NULL));

    // print one number in range 1 ~ 50
    cout << rand() % 50 + 1 << endl;

    return 0;
}


Python

  • random패키지 사용
  • random.random(): 0이상 1미만의 실수를 반환
  • random.randrange(a,b): a이상 b미만의 정수를 반환
import random

# value between 0 and 1, 1 is not included
# ex) 0.667165525151183
rand_value = random.random()

# random integer between a and b, b is not included
rand_value = random.randrange(1,50) # 1 ~ 49


Javascript

  • Math.random()를 사용
  • Math.random(): 0이상 1미만의 실수를 반환

// value between 0 and 1, 1 is not included
// ex) 0.8645079349832587
var = Math.random();


// 올림, 내림, 반올림 -> ceil, floor, round
Math.ceil (Math.random()) // 1
Math.floor (Math.random()) // 0
Math.round (Math.random()) // it depends


Java

  • java.util.Random 혹은 java.lang.Math를 이용
  • random.nextInt(n): 0 ~ n-1 사이의 난수 정수 값을 반환
  • Math.random(): 0이상 1미만의 실수를 반환
import java.util.Random;

public class Main {
    public static void main(String[] args) {
        // Random example using java.util.Random
        Random random = new Random();
        // Number between 100 and 199
        System.out.println(random.nextInt(100) + 100);


        // Random example using java.
        // Number between 100 and 199
        System.out.println( (int)(Math.random() * 114124)%100 + 100 );
    }
}


Shell Script

  • $RANDOM를 이용
  • $RANDOM: 0 ~ 32767 사이의 정수 값을 반환

# Number between 0 and 32767
echo $RANDOM

# Number between 100 and 199
echo $(($RANDOM*12345%100 + 100))


참고자료