2022. 5. 9. 22:36ㆍTIL💡/Algorithms
💚 6603 로또
하나의 배열을 재귀적으로 사용하는 방법이다.
문제에서는 6개를 사전순으로 출력하라고 했다. 그런데 오름차순으로 배열이 정렬되어있기 때문에 아래와 같은 방식으로 재귀적으로 탐색하면 자동으로 사전순으로 출력하게 된다.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> lotto;
void solve(vector<int> &a, int index, int cnt) {
if(cnt == 6) {
for(int num : lotto) {
cout << num << ' ';
}
cout << '\n';
return;
}
int n = a.size();
if(n == index) return;
// 배열이 오름차순으로 정렬되어 있었기에 사전순으로 출력
lotto.push_back(a[index]);
solve(a, index + 1, cnt + 1);
lotto.pop_back();
solve(a, index + 1, cnt);
}
int main() {
while(true) {
int n;
cin >> n;
if(n == 0) break;
vector<int> a(n);
for(int i = 0;i < n;i++) {
cin >> a[i];
}
solve(a, 0, 0);
cout << '\n';
}
}
💚 1182 부분수열의 합
크기가 크지 않으니 모든 수열을 만들어도 된다.
go(index, sum)
- index: 부분 수열에 포함할지 말지 결정해야 하는 인덱스
- sum: 현재까지 부분수열의 합
정답을 찾은 경우
- index == n && sum == s
불가능한 경우
- index == n && sum != s
다음 경우
- index번째 수를 부분수열에 추가: go(index + 1, sum + a[i])
- index번째 수를 부분수열에 추가하지 않음: go(index + 1, sum)
크기가 양수인 경우만 포함해야하기 때문에 크기가 0인 경우는 빼기 위해서 0이 도출되는 경우에는 -1을 해야 한다.
💚 14888 연산자 끼워넣기
이건 앞선 순열 문제로도 풀었는데, 재귀로도 푸는 게 가능하다.
go(a, index, cur, plus, minus, mul, div)
- a: 입력으로 주어진 순열
- index: 현재 계산해야 하는 인덱스(미묘한 정의가 중요)
- cur: index - 1번째까지 계산한 결과
- plus, ... div: 사용할 수 있는 연산자의 개수
#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
using namespace std;
// 최대, 최소 값 리턴
pair<int, int> calc(vector<int> &a, int index, int cur, int plus, int minus, int mul, int div) {
int n = a.size();
if(index == n) {
return make_pair(cur, cur);
}
vector<pair<int, int>> res;
if(plus > 0) {
res.push_back(calc(a, index + 1, cur + a[index], plus - 1, minus, mul, div));
}
if(minus > 0) {
res.push_back(calc(a, index + 1, cur - a[index], plus, minus - 1, mul, div));
}
if(mul > 0) {
res.push_back(calc(a, index + 1, cur * a[index], plus, minus, mul - 1, div));
}
if(div > 0) {
res.push_back(calc(a, index + 1, cur / a[index], plus, minus, mul, div - 1));
}
// 초기화
auto ans = res[0];
for(auto p : res) {
// 최대값
if(ans.first < p.first) {
ans.first = p.first;
}
// 최소값
if(ans.second > p.second) {
ans.second = p.second;
}
}
return ans;
}
int main() {
int n;
cin >> n;
vector<int> a(n);
for(int i = 0;i < n;i++) {
cin >> a[i];
}
int plus, minus, mul, div;
cin >> plus >> minus >> mul >> div;
// index = 현재 계산해야 하는 인덱스
auto p = calc(a, 1, a[0], plus, minus, mul, div);
cout << p.first << '\n';
cout << p.second << '\n';
}
💚 14500 테트로미노
- 폴리오미노는 크기가 1 * 1인 정사각형을 여러 개 이어 붙여서 만든 도형
- 정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 총 5가지가 있다.
- N * M 크기의 종이 위에 테트로미노를 하나 놓아서 놓인 칸의 쓰여 있는 수의 합을 최대로 하는 문제
- 4 <= N, M <= 500
4가지의 테트로미노는 첫번째 블럭에서 3 연속으로 나아가서 만들 수 있지만, 나머지 하나는 불가능하다. 즉 예외 발생!
어떻게 처리할까? 별도로 수동체크 하자.
#include <iostream>
using namespace std;
int a[500][500];
bool c[500][500];
int n, m;
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int ans = 0;
bool inside(int x, int y) {
return x >= 0 && x < n && y >= 0 && y < m;
}
void go(int x, int y, int sum, int cnt) {
if(cnt == 4) {
if(ans < sum) ans = sum;
return;
}
if(!inside(x, y)) {
return;
}
// 이미 방문한 블럭이면 패스
if(c[x][y]) return;
// DFS와 다른 일반적인 브루트 포스의 특징 (체크했다가 탐색 후 해제)
// 아닌 경우도 물론 존재한다.
c[x][y] = true;
for(int k = 0;k < 4;k++) {
go(x + dx[k], y + dy[k], sum + a[x][y], cnt + 1);
}
c[x][y] = false;
}
int main() {
cin >> n >> m;
for(int i = 0;i < n;i++) {
for(int j = 0;j < m;j++) {
cin >> a[i][j];
}
}
for(int i = 0;i < n;i++) {
for(int j = 0;j < m;j++) {
go(i, j, 0, 0);
if(j + 2 < m) {
int temp = a[i][j] + a[i][j + 1] + a[i][j + 2];
if(i - 1 >= 0) {
int temp2 = temp + a[i- 1][j + 1];
if(temp2 > ans) ans = temp2;
}
if(i + 1 < n) {
int temp2 = temp + a[i + 1][j + 1];
if(temp2 > ans) ans = temp2;
}
}
if(i + 2 < n) {
int temp = a[i][j] + a[i + 1][j] + a[i + 2][j];
if(j + 1 < m) {
int temp2 = temp + a[i + 1][j + 1];
if(ans < temp2) ans = temp2;
}
if(j - 1 >= 0) {
int temp2 = temp + a[i + 1][j - 1];
if(ans < temp2) ans = temp2;
}
}
}
}
cout << ans << '\n';
return 0;
}
💚 16197 두 동전
- N * M 크기의 보드, 4개의 버튼이 있다.
- 칸은 비어있거나, 동전, 벽이다.
- 동전은 2개이다.
- 버튼은 왼쪽, 오른쪽, 위, 아래이고, 누르면 그 방향으로 이동한다.
- 이동하려는 칸이 벽이면 이동하지 않는다.
- 이동하려는 칸이 없으면 보드 바깥으로 떨어진다.
- 그 외에는 이동한다.
- 두 동전 중 하나만 보드에 떨어뜨리기 위해 버튼을 몇 번 눌러야 하는가?
- 10번보다 많이 눌러야 하면 -1을 출력한다.
총 4^10 가지의 방법을 수행한다.
go(step, x1, y1, x2, y2)
- step: 버튼을 누른 횟수
- (x1, y1): 한 동전의 위치
- (x2, y2): 다른 동전의 위치
불가능한 경우
- step == 11
- 두 동전이 모두 떨어지는 경우
정답인 경우
- 두 동전 중 하나만 떨어지는 경우
다음 경우
- go(step + 1, nx1, ny1, nx2, ny2)
💚 16198 에너지 모으기
처음에 N개의 구슬이 존재하면 (N - 2) 개의 구슬을 고를 수 있고, 마지막에는 3개의 구슬이 남아서 가운데 구슬만 고를 수 있다.
그래서 경우의 수는 (N - 2)!이다.
#include <iostream>
#include <vector>
using namespace std;
int go(vector<int> &a) {
int n = a.size();
if(n == 2) return 0;
int ans = 0;
for(int i = 0;i < n -1;i++) {
int energy = a[i - 1] * a[i + 1];
vector<int> b(a);
b.erase(b.begin() + i);
energy += go(b);
if(ans < energy) {
ans = energy;
}
}
return ans;
}
int main() {
int n;
cin >> n;
vector<int> a(n);
for(int i = 0;i < n;i++) {
cin >> a[i];
}
cout << go(a) << '\n';
return 0;
}
다음 강의는 백트래킹 관련 재귀 문제이다.
'TIL💡 > Algorithms' 카테고리의 다른 글
[알고리즘 중급] 비트마스크 (0) | 2022.05.10 |
---|---|
[알고리즘 중급] 재귀 - 2 (0) | 2022.05.10 |
[알고리즘 중급] 순열(연습) (0) | 2022.05.06 |
[백준] 1941: 소문난 칠공주 (0) | 2022.05.04 |
[백준] 16472: 고냥이 (0) | 2022.05.04 |