완전 탐색
완전 탐색은 말 그대로 모든 경우의 수를 탐색하는 방법으로 브루트 포스라고 불린다(brute-force)
완전 탐색의 방법은 반복문과 재귀함수로 나뉜다.
반복문
반복문으로 가능하다면 반복문으로 하는 것이 좋다.
재귀함수
너무 복잡하거나 어떠한 행위는 반복하는데 매개변수만 수정해서 넘기면 될 것 같은 경우에 시행한다.
예시 문제
100이하의 수 조합이 주어질 때 소수가 되는 경우의 수를 모두 구하여라
입력예시:
10
24 35 38 40 49 59 60 67 83 98
코드 구현
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
vector<int> v;
vector<int> s;
bool isPrime(int n) {
if (n == 0)
return false;
for (int i = 2; i < n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
int go(int idx, int sum, int n) {
if (idx == n)
return isPrime(sum);
return go(idx + 1, sum+v[idx], n) + go(idx + 1, sum, n);
}
int main() {
int n, num;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> num;
v.push_back(num);
}
cout<< go(0, 0, n);
}
/*
10
24 35 38 40 49 59 60 67 83 98
*/
예시문제
N과 N개의 자연수가 주어진다.
여기서 몇개의 숫자를 골라 합을 mod11을 했을 때 나오는 가장 큰수를 구하라.
입력예시:
10
24 35 38 40 49 59 60 67 83 98
코드구현
#include <iostream>
#include<iostream>
#include <vector>
#include <algorithm>
#include<math.h>
using namespace std;
vector<int> v;
int N, ret = 0;
void BigSearch(int sum, int idx) {
if (idx == N)
{
ret = max(ret, sum%11 );
return;
}
BigSearch(sum + v[idx], idx + 1);
BigSearch(sum, idx + 1);
}
int main() {
int num;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> num;
v.push_back(num);
}
BigSearch(0, 0);
cout << ret;
}
/*
10
24 35 38 40 49 59 60 67 83 98
*/
백트래킹
완전탐색에 가지치기를 더해 불필요한 탐색을 최소화 시킨 것을 말한다.
또한 dfs의 변형적인 형태로 가지치기를 추가했다고 볼 수 있다.
아까의 그 코드를 다시 살펴보자
#include <iostream>
#include<iostream>
#include <vector>
#include <algorithm>
#include<math.h>
using namespace std;
vector<int> v;
int N, ret = 0, cnt=0;
void BigSearch(int sum, int idx) {
if (idx == N)
{
ret = max(ret, sum%11 );
cnt++;
return;
}
BigSearch(sum + v[idx], idx + 1);
BigSearch(sum, idx + 1);
}
int main() {
int num;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> num;
v.push_back(num);
}
BigSearch(0, 0);
cout << ret<<"\n";
cout << cnt;
}
/*
10
24 35 38 40 49 59 60 67 83 98
*/
출력이 10 1024가 나오는 것을 볼 수 있다. 즉 2의 10승 번 탐색을 진행한 것이다.
그렇다면 이것을 어떻게 가지치기 할 수 있을까?
바로 mod의 성질을 이용하는 것이다. mod11을 했을 때 최대로 나올 수 있는 것은 10이다.
그때 탐색을 종료시켜 주면 된다.
#include <iostream>
#include<iostream>
#include <vector>
#include <algorithm>
#include<math.h>
using namespace std;
vector<int> v;
int N, ret = 0, cnt=0;
void BigSearch(int sum, int idx) {
if (ret == 10)
return;
if (idx == N)
{
ret = max(ret, sum%11 );
cnt++;
return;
}
BigSearch(sum + v[idx], idx + 1);
BigSearch(sum, idx + 1);
}
int main() {
int num;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> num;
v.push_back(num);
}
BigSearch(0, 0);
cout << ret<<"\n";
cout << cnt;
}
/*
10
24 35 38 40 49 59 60 67 83 98
*/
이렇게 하면 탐색 수가 10으로 줄어든 것을 볼 수 있다.
++완전 탐색 원상 복구
모든 경우의 수를 탐색하는 경우 다시 미방문 처리하고 탐색해야 하는 경우가 있다.
예시 문제
나는 3*3맵에서 0,0 지점에 있다. 목적지는 2,2인데 가는 길에 점수를 얻는다. 점수를 얻는 모든 경우의 수를 구해라
맵:
{10, 20, 21},
{70, 90, 12},
{80, 110, 120}
코드 구현
#include <iostream>
#include<iostream>
#include <vector>
#include <algorithm>
#include<math.h>
using namespace std;
vector<int> v;
const int n = 3;
int visit[3][3] = { 0 };
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int board[3][3] = {
{10, 20, 21},{70, 90, 12},{80, 110, 120}
};
void dfs(int x, int y) {
if (x == 2 && y == 2) //도착한 경우 출력
{
for (int i : v)
cout << i << " ";
cout << "\n";
return; //기저사례
}
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || nx > 3 || ny < 0 || ny > 3)
continue;
if (visit[ny][nx])
continue;
v.push_back(board[ny][nx]);
visit[ny][nx] = 1;
dfs(nx, ny);
//다시 원상복구
v.pop_back();
visit[ny][nx] = 0;
}
}
int main() {
dfs(0, 0);
}
'알고리즘' 카테고리의 다른 글
[비트 마스킹][c++] 개념과 활용 방법에 대해서 알아보자 (0) | 2024.07.09 |
---|---|
[자료구조][스택] 중위 수식 변환 프로그램 c구현 (0) | 2024.05.05 |
[백준] 모듈러 연산 정의 및 활용 (3) | 2024.04.29 |
[자료구조][배열][스택] c 코드 구현 (0) | 2024.04.28 |
[자료구조][집합] c구현 재귀/ 반복문 (3) | 2024.04.16 |