Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

09 백트래킹 #8

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions 09_백트래킹/14888.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 14888번 연산자 끼워넣기: https://www.acmicpc.net/problem/14888
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int INF = 1e9;
const int MAX_N = 11;//입력받을 수 있는 값을 최대 개수
const int ADD = 0, SUB = 1, MUL = 2, DIV = 3;//각 숫자가 연산을 가르킴
const int EXP_NUM = 4;//연산 경우는 4가지밖에 없으므로 4로 설정

int n;
int nums[MAX_N];
int expression[EXP_NUM];
int max_val = -INF, min_val = INF;

/**
* 연산자를 하나씩, 총 (N-1)개가 될 때까지 뽑는다.
*
* cnt: 뽑은 연산자의 개수
* curr_val: 현재 연산값
*/
void backtrack(int cnt, int curr_val) {
// 재귀 호출 종료 조건: (N-1)개의 연산자를 다 뽑은 경우
if (cnt == n - 1) {//뽑은 연산자 수가 입력받은 숫자-1이라면
max_val = max(max_val, curr_val);//최대값
min_val = min(min_val, curr_val);//최소값
return;
}

// i: 연산자 번호
for (int i = 0; i < EXP_NUM; i++) {
// 사용할 연산자가 남아있지 않으면, 사용 불가
if (expression[i] == 0) {//연산자 개수가 없으면 반복문으로 돌아가기
continue;
}

// 연산자 사용
expression[i]--;//연산자 개수 하나 줄이기
int new_sum = 0;
switch (i) {
case ADD://i가 0이면
new_sum = curr_val + nums[cnt + 1];//뽑은 연산자 +1 위치에 있는 값 더하기
break;
case SUB://i가 1이면
new_sum = curr_val - nums[cnt + 1];//뽑은 연산자 +1 위치에 있는 값 빼기
break;
case MUL://i가 2이면
new_sum = curr_val * nums[cnt + 1];//뽑은 연산자 +1 위치에 있는 값 곱하기
break;
case DIV://i가 3이면
new_sum = curr_val / nums[cnt + 1];//뽑은 연산자 +1 위치에 있는 값 나누기
break;
}

// 다음 연산자 선택
backtrack(cnt + 1, new_sum);//재귀적으로 반복

// 연산자 반납
expression[i]++;
}
}

/**
* 모든 연산자 조합을 시도해보면서 최대값과 최솟값을 찾는다.
* 모든 연산자 조합을 만들기 위해 가장 왼쪽에 들어갈 연산자부터 하나씩 선택한다.
*/
int main() {
// 입력
cin >> n;//수의 개수 입력받기
for (int i = 0; i < n; i++) {//입력받은 수의 개수만큼
cin >> nums[i];//숫자 입력받기
}
for (int i = 0; i < EXP_NUM; i++) {//연산자 개수 입력받기
cin >> expression[i];
}

// 연산
backtrack(0, nums[0]);

// 출력
cout << max_val << '\n' << min_val;

return 0;
}
47 changes: 47 additions & 0 deletions 09_백트래킹/15665.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 15665번 N과 M(11): https://www.acmicpc.net/problem/15665
#include <iostream>
#include <vector>
#include <set>

using namespace std;

set<int> numbers; // 입력 받은 수
vector<int> sequence; // 출력할 수열

/**
* 입력 받은 수에서 중복수열을 만든다.
*
* m: 수열의 길이
* cnt: 현재 뽑은 수의 개수
*/
void backtracking(int m, int cnt) {
// 재귀 호출 종료 조건: m개의 수를 모두 뽑음
if (cnt == m) {//현재 뽑은 수의 개수가 수열의 길이와 같다면
// 수열 출력
for (int i = 0; i < m; i++)
cout << sequence[i] << ' ';//수열 출력하기
cout << '\n';
return;
}
// 중복을 허용해서 하나씩 수를 뽑아 수열에 저장
for (auto num: numbers) {
sequence[cnt] = num;//수열에 값 저장
backtracking(m, cnt + 1);//재귀적으로 반복
}
}

int main() {
// 입력
int n, m;
cin >> n >> m;//n과 m값 입력받기
while (n--) {//n이 0이 아니라면
int num;
cin >> num;//값 입력ㅂㄷ기
numbers.insert(num);//입력받은 수 집합에 값 집어넣기
}
// 초기화
sequence.assign(m, 0);
// 연산
backtracking(m, 0);
return 0;
}
109 changes: 109 additions & 0 deletions 09_백트래킹/20055.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include <deque>
#include <iostream>

using namespace std;

struct info { // 내구도와 로봇 존재 여부
int power;//내구도
bool is_on;//로봇존재여부
};

// 벨트를 한 칸 회전
void rotateBelt(deque<info> &belt, int n) {
belt.push_front(belt.back());//맨 뒤의 값을 맨 앞으로 보내기
belt.pop_back();//맨 뒤의 값 삭제하기
belt[n - 1].is_on = false; // 로봇이 내리는 위치
}

// 로봇을 움직일 수 있으면 한 칸 이동
void moveRobot(deque<info> &belt, int n) {
for (int i = n - 2; i >= 0; i--) {
if (!belt[i].is_on) {//로봇이 없을 때
continue;
}

if (!belt[i + 1].is_on && (belt[i + 1].power >= 1)) {// 다음 칸에 로봇이 없으면서 다음 칸에 내구도가 남아있을 때
belt[i].is_on = false;//현재 벨트에는 로봇 삭제하고
belt[i + 1].is_on = true;//다음 벨트에 로봇이동
belt[i + 1].power--;//내구도 1감소
}

belt[n - 1].is_on = false; // 로봇이 내리는 위치
}
}

// 올리는 칸에 로봇을 올릴 수 있으면 올림
void putRobot(deque<info> &belt) {
if (!belt[0].is_on && belt[0].power >= 1) {
// 올리는 칸에 로봇이 존재하지 않고, 내구도가 남아있으면
belt[0].is_on = true;
belt[0].power--;
}
}

// 벨트의 내구도 체크
bool checkFinish(deque<info> &belt, int n, int k) {
int count = 0;//내구도가 0인 벨트 체크

for (int i = 0; i < 2 * n; i++) {
if (belt[i].power == 0) {//내구도가 0이되면 count값 1증가
count++;
}
}

return count >= k;//내구도가 0인 칸이 k개 이상인지 확인
}

int solution(deque<info> &belt, int n, int k) {
int step = 1;//현재 몇번째 단계인지 기록
while (true) {
rotateBelt(belt, n);//회전
moveRobot(belt, n);//이동
putRobot(belt);//로봇 올리기

// 내구도 체크하기
if (checkFinish(belt, n, k)) {
return step;//현재 단계 반환
}
step++;//단계 1증가
}
}


/**
* [컨베이어 벨트 위의 로봇 문제]
* 1. 벨트가 각 칸 위의 로봇과 함께 한 칸 회전
* 2. 가장 먼저 벨트에 올라간 로봇부터, 벨트 회전 방향으로 한 칸 이동할 수 있다면 이동
* (이동가능: 이동하려는 칸에 로봇이 없고, 그 칸의 내구도가 1 이상이어야 함)
* 3. 올리는 위치에 있는 칸의 내구도가 0이 아니면 올리는 위치에 로봇 올림
* 4. 내구도가 0인 칸의 개수가 k개 이상이라면 과정 종료. 그렇지 않다면 1로 돌아감
* -> 1 ~ 3까지가 1단계
*
* [문제 풀이]
* 회전과 관련이 깊은 자료구조 deque를 사용하여 풀이
*
* 1번 벨트 회전: 벨트의 마지막 원소를 벨트 처음에 넣기
* 2번 로봇 이동: 가장 먼저 올라간 로봇부터 고려해야 하므로 (내리는 위치 - 1)부터 (올리는 위치)까지 검사
* -> 로봇 옮기는거 가능하면 존재여부 체크하고 내구도 감소
* 3번 로봇 추가: 올리는 위치 칸 내구도 0이 아니라면 해당 칸 로봇 존재 여부 체크 + 내구도 감소
*
*
* >> 주의: 칸 번호를 1번이 아닌 0번부터 시작하는 것으로 관리하고 있기 때문에, n번 칸이 아니라 n-1번 칸이 내리는 위치 <<
*/

int main() {
// 입력
int n, k;
cin >> n >> k;//n과 k입력받기
deque<info> belt(2 * n); // 컨베이어 벨트의 내구도와 로봇 존재 여부 저장
for (int i = 0; i < 2 * n; i++) {//2n만큼 반복
cin >> belt[i].power;//벨트 내구 입력받기
belt[i].is_on = false;//값 false로 처기화
}

// 연산
int answer = solution(belt, n, k);//연산 함수 호출

// 출력
cout << answer;
}