완전탐색의 정의

  • 모든 경우의 수를 전부 찾아서 답을 찾는 알고리즘을 말한다.

  • Brute Force 알고리즘 이라고도 불린다.

  • 모든 경우를 전부 찾기때문에 정확한 답을 찾을 수 있다.

  • 모든 경우를 전부 찾기때문에 시간초과에 걸릴 가능성이 높다 -> 이를 해결하기 위해 백트래킹 사용


완전탐색의 종류

① for문을 통한 완전탐색

더보기

 

합이 10인 순서쌍을 구하는 문제

 

 

#include <stdio.h>
#include <iostream>
#include <string>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
 
int main(void)
{
    int sum = 10;
    int cnt = 0;
    
    for(int i = 0; i <= 10; i++)
    {
        for(int j = 0; j <= 10; j++)
        {
            if(i + j == sum)
            {
                cnt++;
            }
        }
    }
    
    cout << cnt;
    
    return 0;
}
 
cs

② 재귀함수를 통한 완전탐색 -> 순열, 조합

더보기

 

조합 문제 - 일곱 난쟁이 https://www.acmicpc.net/problem/2309

 

 

#include <stdio.h>
 
int height[7];
int input[9];
int flag;
 
void combination(int sum, int cnt, int idx)
{        
    if(cnt == 7)
    {        
        if(sum == 100)
        {
            flag = 1;
            return;
        }
        else
        {
            return;
        }
    }
    
    for(int i = idx; i < 9; i++)
    {
        height[cnt] = input[i];
        combination(sum + height[cnt], cnt+1, i+1);
        if(flag == 1)
        {
            return;
        }
    }
}
 
int main(void)
{
//    freopen("B2309_input.txt", "r", stdin);
    
    for(int i = 0; i < 9; i++)
    {
        scanf("%d"&input[i]);
    }
    
    combination(000);
    
    // 버블 정렬 -> 오름차순 출력을 위해 
    for(int i = 6; i > 0; i--)
    {
        for(int j = 0; j < i; j++)
        {
            if(height[j] > height[j+1])
            {
                int temp = height[j];
                height[j] = height[j+1];
                height[j+1= temp;
            }
        }
    }
    
    for(int i = 0; i < 7; i++)
    {
        printf("%d\n", height[i]);
    }
    
    return 0;
}
c

③ 라이브러리를 통한 완전탐색 -> 순열, 조합

   C++ :  https://twpower.github.io/90-combination-by-using-next_permutation

 

   PYTHON : https://ghwlchlaks.github.io/permutation-combination-python


프로그래머스 완전탐색 문제

숫자 야구 (조합 응용)

#include <string>
#include <vector>
#include <iostream>
using namespace std;
 
int num[3];
int result[3];
int visited[10];
int answer;
 
vector<vector<int>> copy_baseball;
 
int strike()
{
    int strikeCnt = 0;
    
    for(int i = 0; i < 3; i++)
    {            
        if(result[i] == num[i])
        { 
            strikeCnt++;
        }
    }
    
    return strikeCnt;
}
 
int ball()
{
    int ballCnt = 0;
    
    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j < 3; j++)
        {
            if(i == j)
            {
                continue;
            }
 
            if(result[i] == num[j])
            {
                ballCnt++;
            }
        }
    }
    
    return ballCnt;
}
 
void DFS(int cnt)
{
    if(cnt == 3)
    {   
        for(int i = 0; i < copy_baseball.size(); i++)
        {
            // 자리수별로 num에 저장
            int temp = copy_baseball[i][0];
            for(int j = 2; j >= 0; j--)
            {
                num[j] = temp % 10;
                temp /= 10;
            }
            
            // 스트라이크
            if(strike() != copy_baseball[i][1])
            {
                return;
            }
            
            // 볼
            if(ball() != copy_baseball[i][2])
            {
                return;
            }    
        }
        
        answer++;   
        
        return;
    }
    
    for(int i = 1; i <= 9; i++)    
    {
        if(visited[i] == 0)
        {
            visited[i] = 1;
            result[cnt] = i;
            DFS(cnt+1);   
            visited[i] = 0;
        }
    }
}
 
int solution(vector<vector<int>> baseball) 
{
    copy_baseball = baseball;
    
    DFS(0);
    
    return answer;
}
cs

추천문제

https://www.acmicpc.net/problem/2529 (순열)

 

https://www.acmicpc.net/problem/10819 (순열) 

 

https://www.acmicpc.net/problem/2309 (조합) 

 

https://www.acmicpc.net/problem/15686 (조합) -> 삼성 코테 기출

 

알고리즘 문제를 풀다가 main 함수에 int arr[5000][5000]을 선언했는데 바로 종료가 되어서... 검색해봤다.

 

함수 내에서 선언되어 사용되는 배열의 경우 배열 자체가 스택 영역에 생성되고, 함수가 호출될 때 마다 해당 배열을 위한 메모리를 새로 생성하고 초기화 하는 작업을 하기 때문에 배열의 크기가 큰 경우에는 다른 방법을 사용해야 한다.

함수 내에서 크기가 큰 배열을 생성하는 경우 다음과 같은 문제점을 일으킬 수 있다

  • 배열을 초기화 하는 경우 함수가 호출될 때 마다 값을 채워넣어야 하기 때문에 프로그램의 수행 속도가 느려질 수 있다. 특히나 자주 호출되는 함수인 경우에는 프로그램의 속도를 치명적으로 느리게 만들 수 도 있다.
  • 이론적으로 스택 -- 지역 자동변수가 저장되는 영역의 크기는 제한되지 않지만, 시스템에 따라 하드웨어적인 제한이나 스택 운영의 효율을 높이기 위해 스택의 크기를 제한하기도 한다. 그렇기 때문에 함수 내에서 선언되는 배열의 크기가 과도하게 큰 경우 스택 오버플로우 에러가 발생하고 프로그램이 강제 종료 될 수 도 있다.
  • 컴파일러의 종류나 옵션에 따라서는 컴파일된 프로그램의 크기가 커질 수 도 있다.

그래서 배열의 크기가 조금이라도 크다는 느낌이 든다면 다른 메모리 영역을 사용하는 것을 고려해 볼 필요가 있다

  • 전역 변수나 스태틱 변수를 사용한다.
  • alloc() 계열의 함수를 이용하여 힙영역을 할당하여 사용한다.

출처 :

https://ko.wikibooks.org/wiki/C_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D_%EC%9E%85%EB%AC%B8/%EB%8D%B0%EC%9D%B4%ED%84%B0_%EB%B0%B0%EC%97%B4

 

프림 알고리즘 (최소스패닝트리)

  • 그래프상에 존재하는 모든 노드들을 최소비용으로 연결시키는 알고리즘

 

프림 알고리즘을 적용한 모습

#include <stdio.h>
#include <vector>
#include <queue>
#include <functional>
using namespace std;
 
int visited[10001];
int V, E;
int ans;
 
vector<pair<intint>> map[10001];
 
void prim(int start)
{
    visited[start] = 1;
    
    priority_queue<pair<intint>vector<pair<intint>>, greater<pair<intint>>> pq;
    
    for(int i = 0; i < map[start].size(); i++)
    {
        int next = map[start][i].first;
        int nextCost = map[start][i].second;
        
        pq.push(make_pair(nextCost, next));
    }
    
    while(!pq.empty())
    {
        int now = pq.top().second;
        int nowCost = pq.top().first;
        pq.pop();
        
        if(visited[now] == 1)
        {
            continue;
        }
        
        visited[now] = 1;
        
        ans += nowCost;
        
        for(int i = 0; i < map[now].size(); i++)
        {
            int next = map[now][i].first;
            int nextCost = map[now][i].second;
            
            pq.push(make_pair(nextCost, next));
        }
    }
}
 
int main(void)
{
    freopen("B1197_input.txt""r", stdin);
    
    scanf("%d %d"&V, &E);
    
    for(int i = 0; i < E; i++)
    {
        int from, to, weight;
        scanf("%d %d %d"&from, &to, &weight);
        
// 무향
        map[from].push_back(make_pair(to, weight));
        map[to].push_back({from, weight});
    }
    
    prim(1);
    
    printf("%d", ans);
    
    return 0;
}
 
cs
 

 

 

다익스트라 알고리즘 (최단경로트리)

  • 시작 노드(1)로부터 그래프 상에 존재하는 모든 노드, 즉 두 노드 사이의 최단거리를 구하는 알고리즘

 

다익스트라 알고리즘을 적용한 모습

#include <stdio.h>
#include <vector>
#include <queue>
#include <functional> // 우선순위큐의 greater
using namespace std;
 
int d[20001];
int V, E;
int start;
 
vector<pair<intint>> v[20001];
 
void solve(int start)
{
    d[start] = 0;
    priority_queue<pair<intint>vector<pair<intint>>, greater<pair<intint>>> pq;
    pq.push({0, start});
    
    while(!pq.empty())
    {
        int now = pq.top().second;
        int nowCost = pq.top().first;
        pq.pop();
        
        if(d[now] != nowCost)
        {
            continue;
        }
        
        for(int i = 0; i < (int)v[now].size(); i++)
        {
            int next = v[now][i].first;
            int nextCost = d[now]+v[now][i].second;
            
            if(nextCost < d[next])
            {
                d[next] = nextCost;
                pq.push({nextCost, next});
            }
        }
    }
}
 
int main(void)
{
    freopen("B1753_input.txt""r", stdin);
    
    scanf("%d %d %d"&V, &E, &start);
    
    for(int i = 1; i <= V; i++)
    {
        d[i] = 9999999;
    }
 
    for(int i = 1; i <= E; i++)
    {
        int from, to, weight;
        scanf("%d %d %d"&from, &to, &weight);    
        
 // 유향
        v[from].push_back({to, weight});
    }
    
    solve(start);
    
    for(int i = 1; i <= V; i++)
    {
        if(d[i] == 9999999)
        {
            printf("INF\n");
        }
        else
        {
            printf("%d\n", d[i]);    
        }
    }
    
    return 0;
}
 
cs

 

차이점

1. 프림은 다익스트라와 달리 두 노드 사이가 최단거리가 아닐 수도 있다.

   ※ 프림은 1->3의 비용이 3인 반면에, 다익스트라는 1->3의 비용이 2이다.

2. 프림은 무향 그래프에서만 작동하고, 다익스트라는 무향, 유향 그래프에서 모두 작동한다.

3. 프림이 다익스트라를, 다익스트라가 프림을 보장해주지 않는다.

   -> 최소스패닝트리가 최단경로트리를, 최단경로트리가 최소스패닝트리를 보장하지 않는다.

 

다익스트라 알고리즘

  • 한정점에서 모든정점까지의 거리를 알고 싶을때 사용

 

플로이드와샬 알고리즘

  • 모든정점에서 모든정점까지의 거리를 알고 싶을때 사용

 

'Algorithm' 카테고리의 다른 글

완전탐색  (0) 2020.01.27
지역변수로 크기가 큰 배열 선언 시 문제점  (0) 2019.09.27
Prim vs Dijkstra (프림 다익스트라 비교)  (4) 2019.09.23

+ Recent posts