DFS-연산자 끼워넣기
문제
N개의 수로 이루어진 수열 A1, A2, …, AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다.
연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.
우리는 수와 수 사이에 연산자를 하나씩 넣어서, 수식을 하나 만들 수 있다. 이때, 주어진 수의 순서를 바꾸면 안 된다.
핵심 아이디어
‘가능한 모든 조합을 만드는’ DFS
가능한 모든 연산자 조합을 찾는 것, 즉‘순열’을 만드는 것이 관건이다.
해당 연산자를 찾으며 함께 value값에 nums 요소들을 계산 & 누적해나가고
연산자를 다 사용했을 때 누적 값을 최소, 최대로 갱신한다.
Points
DFS + 넣고 빼기 트릭
순열을 만들 땐 DFS + 넣고 빼기 트릭을 사용한다.
- 특정 요소 투입 (ex) +)
- dfs 수행 : 해당 특정 요소까지 확정, 그 이후의 순열값을 계산
- 특정 요소 빼고 다른 요소 넣어서 조합 구하기
이 트릭을 사용하기 위해 DFS 인자로 1. depth와 2. 현재까지의 누적 value를 재귀적으로 넘겨줘야 한다.
종료 조건으로 depth가 기준 값에 달성하였거나 value가 특정 조건을 만족할 때 재귀를 종료한다.
유사한 문제
[DFS-여행경로 | 희민이의 개발노트 (vida0822.github.io)](https://vida0822.github.io/algorithm/Algorithm_여행경로/) |
Tip
재귀적 구조의 코드에서, 재귀적으로 호출할 때마다 달라지는 값이라면 인자로 넘겨주고,
어디서든 동일한 값을 유지한다면 static 변수로 선언해준다
코드
import java.util.*;
class Main{
public static int n ;
public static ArrayList<Integer> arr = new ArrayList<>() ;
public static int add, sub, mul, div ;
public static int minValue = (int)1e9;
public static int maxValue = (int)-1e9 ;
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt() ;
// 숫자(n개)
for(int i = 0 ; i < n ; i++){
arr.add( sc.nextInt()) ;
}
// 연산자 (n-1)개
add = sc.nextInt() ;
sub = sc.nextInt() ;
mul = sc.nextInt() ;
div = sc.nextInt() ;
// DFS 호출
dfs(1, arr.get(0)) ;
// 최댓값, 최솟값 출력
System.out.println(maxValue) ;
System.out.println(minValue) ;
}
public static void dfs(int i, int now){ // 자릿수, 현재까지 계산값
// 모든 연산자를 다 사용한 경우, 최솟값&최댓값 업데이트
if(i == n){
minValue = Math.min(minValue, now);
maxValue = Math.max(maxValue, now);
}else{
// ** 구조 암기 : 순열 만들기
if(add > 0){
add -= 1 ;
dfs(i+1, now+arr.get(i)) ;
add += 1 ;
}
if(sub > 0){
// else if 가 아니기 때문에 add if문이 종료되면 같은 자릿수에서 sub가 실행된다.
// 즉 모든 4개의 분기문이 남은 연산자가 없을 때까지 실행된다.
sub -= 1 ;
dfs(i+1 , now - arr.get(i)) ;
sub += 1;
}
if(mul > 0){
mul -= 1;
dfs(i+1 ,now*arr.get(i)) ;
mul += 1;
}
if(div > 0){
div -= 1 ;
dfs(i+1, now/arr.get(i)) ;
div += 1 ;
}
}
}
}
입력 개선 코드
BufferedReader와 BufferdWriter는 버퍼를 사용하여 읽기와 쓰기를 하는 함수로 Scanner보다 훨씬 성능이 빠르다!
입출력 작업이 많은 경우 BufferedReader/Writer를, 간단할 경우 쓰기 편한 Scanner을 사용한다!
import java.util.* ;
import java.io.* ;
public class Main{
public static int N ;
public static int[] number ;
public static int[] operator = new int[4] ;
public static int MAX = Integer.MIN_VALUE ;
public static int MIN = Integer.MAX_VALUE ;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
N = Integer.parseInt(br.readLine()) ;
number = new int[N] ;
// 숫자 입력받기
StringTokenizer st = new StringTokenizer(br.readLine(), " ") ;
for(int i = 0 ; i < N ; i++){
number[i] = Integer.parseInt(st.nextToken()) ;
}
// 연산자 입력받기
st = new StringTokenizer(br.readLine(), " ") ;
for(int i = 0 ; i < 4 ; i++){
operator[i] = Integer.parseInt(st.nextToken()) ;
}
// 연산자 끼워넣기
dfs(number[0], 1) ; // 재귀 내에서 계산하면서 max, min값 동시에 갱신
// 최대, 최솟값 출력
System.out.println(MAX) ;
System.out.println(MIN) ;
}
public static void dfs(int num, int idx){ // 누적 계산값, 다음 숫자값
if(idx == N){ // 다 끼워넣었을 떄
// static 갱신
MAX = Math.max(MAX, num) ;
MIN = Math.min(MIN, num) ;
return ;
}
for(int i = 0 ; i < 4 ; i++){ // 연산자 하나씩 돌면서
if(operator[i] > 0){
operator[i]-- ;
switch(i){
case 0 : dfs(num + number[idx], idx+1) ; break ;
case 1 : dfs(num - number[idx], idx+1) ; break ;
case 2 : dfs(num * number[idx], idx+1) ; break ;
case 3 : dfs(num / number[idx], idx+1) ; break ;
}
operator[i]++;
}
}
}
}
댓글남기기