※ 웹 환경에 최적화된 서식이므로 웹 페이지로 열람함을 권장.
소풍(링크)
재귀적으로 생각하기
학생의 명단이 주어지면 둘 씩 짝 짓는 경우의 수를 계산한다. 두 학생을 짝 짓는 작업을 하나의 작업 조각으로 보면 재귀 함수는 아직 짝을 찾지 못한 학생의 명단이 주어질 때 두 학생을 짝 지어주는 작업을 반복한다.
중복으로 세는 문제
1번 3번을 짝 짓는 것과 3번 1번을 짝 짓는 것은 별개의 경우로 세면 안 된다. 이처럼 중복으로 세는 문제를 다루는 한가지 방법은 항상 특정 형태의 답만을 세는 것이다. (조합을 구할 때 동일한 원소들이 순서가 다르게 선택되어 별개의 경우가 되지 않도록 고르는 순서를 강제하는 것과 같다.)
무식하게 풀 수 있을까?
이 문제가 가장 많은 답을 가지는 입력은 열 명의 학생이 모두 서로 친구인 경우. 가장 빠른 번호의 학생이 선택 가능한 짝은 아홉, 그 다음 학생은 일곱을 선택할 수 있다. 따라서 9 x 7 x 5 x 3 x 1 = 945 의 상한을 가진다.
#include <iostream>
#include <vector>
using namespace std;
#define MAX_STUDENT_NUM 10
int numOfStudents;
bool weAreFriends[MAX_STUDENT_NUM][MAX_STUDENT_NUM];
int picnic(bool isPaired[MAX_STUDENT_NUM]) {
int front = -1;
for (int i = 0; i < numOfStudents; ++i) {
if (isPaired[i] == false) {
front = i;
break;
}
}
if (front == -1) return 1;
int ret = 0;
for (int i = front + 1; i < numOfStudents; ++i) {
if (isPaired[i] == false && weAreFriends[front][i]) {
isPaired[front] = isPaired[i] = true;
ret += picnic(isPaired);
isPaired[front] = isPaired[i] = false;
}
}
return ret;
}
int main() {
int C; cin >> C;
int numOfPairs;
bool isPaired[MAX_STUDENT_NUM];
vector<int> answers;
while(C-- > 0) {
cin >> numOfStudents >> numOfPairs;
for (auto& e: isPaired) e = false;
for (int i = 0; i < MAX_STUDENT_NUM; ++i)
for (auto& e: weAreFriends[i]) e = false;
for (int i = 0; i < numOfPairs; ++i) {
int f1, f2; cin >> f1 >> f2;
weAreFriends[f1][f2] = weAreFriends[f2][f1] = true;
}
answers.push_back(picnic(isPaired));
}
for (auto e: answers) cout << e << " ";
cout << endl;
return 0;
}
'문제 해결 알고리즘 기초 > 완전 탐색' 카테고리의 다른 글
[알고스팟] 여행하는 외판원 문제 - 완전 탐색, 재귀 (0) | 2020.08.04 |
---|---|
[알고스팟] 게임판 덮기 - 완전 탐색, 재귀 (0) | 2020.07.31 |
[알고스팟] 보글 게임 - 완전 탐색, 재귀 (0) | 2020.07.21 |
완전 탐색으로 n 개 중 r 개 고르기 (조합) (0) | 2020.07.21 |
완전 탐색 알고리즘에 대해서 (0) | 2020.07.21 |