알고리즘/백준
[백준 15663번 N과 M (9)]
익명의 문과 개발자
2024. 8. 16. 11:46
728x90
728x90
오늘의 문제는 N과 M(9)이다.
#고민의 흐름
처음엔 무조건 완전탐색!
수가 중복되지 않도록 visited배열을 만들어 방문처리를 해줌과 동시에,
M만큼 수를 골랐을 때, 이미 고른 조합인지를 확인해줘야 했기 때문에.
다 골랐을 때 이 결과를 String으로 만들어 ArrayList에 contains 함수를 통해 이미 있는지를 확인하고
있다면 넣어주고 StringBuilder에 추가 / 없다면 return을 해주었다.
코드는 다음과 같다.
( 코드가 너무 커보인다면 "Ctrl + 스크롤 내리기" 하면 잘보여요 ! )
( 초기화는 "Ctrl + 0" / 다시 확대는 "Ctrl + 스크롤 올리기" )
import java.util.*;
import java.io.*;
public class Main {
static int N, M, arr[], visited[], ans[];
static ArrayList<String> list = new ArrayList<>();
static String temp = "";
static StringBuilder sb = new StringBuilder();
public static void main (String [] arg) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
arr = new int [N];
visited = new int [N];
ans = new int [M];
st = new StringTokenizer(br.readLine());
for(int i = 0; i<N; i++){
arr[i] = Integer.parseInt(st.nextToken());
}
//여기까지가 입력!
Arrays.sort(arr);
// 문제의 조건에 맞춰 사전순 출력을 위해 오름차순으로 정렬을 해준다.
recur(0, 0);
System.out.print(sb);
}
//cur은 현재 arr의 어느 수인지, idx는 ans배열의 현위치이다.
static void recur(int cur, int idx){
if(cur == M){ // 만약 다찼다면 싹모아 String으로 만들고 이미 있는지 확인.
String temp = "";
for(int i = 0; i<M; i++){
temp += String.valueOf(ans[i]);
}
if(list.contains(temp)){ // 있다면 그냥 return;
return;
}else{ // 없다면 넣어주고 sb에도 추가해준다.
list.add(temp);
for(int i = 0; i<M; i++){
sb.append(ans[i]).append(" ");
}
sb.append("\n");
return;
}
}
//방문했으면 continue;
//안했으면 ans에입력해주고 방문처리해주고 넘어가기 !
//다녀오면 방문처리 풀어주기까지 !
for(int i = 0; i<N; i++){
if(visited[i] != 0) continue;
visited[i] = 1;
ans[idx] = arr[i];
recur(cur + 1, idx + 1);
visited[i] = 0;
}
}
}
하지만 생각과 달리 시간초과라는 결과가 나왔다 ...
다시 생각해보니 N과 M이 최대이고 모두 같은 수라면 중복되는 경우가 너~무 많아짐을 생각할 수 있었다..
중복을 어떻게 없애줄지 고민을 한참하다가 결국 답을 혼자서 찾지 못했다 ..
검색해서 깨달은 다음 풀이방식을 코드와 함께 보자!
import java.util.*;
import java.io.*;
public class Main {
static int N, M, arr[], visited[], picked[];
static StringBuilder sb = new StringBuilder();
public static void main (String [] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
arr = new int [N];
visited = new int [N];
picked = new int [M];
st = new StringTokenizer(br.readLine());
for(int i = 0; i<N; i++){
arr[i] = Integer.parseInt(st.nextToken());
}
Arrays.sort(arr);
//여기까진 같다.
recur(0);
System.out.print(sb);
}
private static void recur(int cur){
if(cur >= M){
for(int i = 0; i<M; i++){
sb.append(picked[i]).append(" ");
}
sb.append("\n");
return;
}
int before = 0; // 이 before 값이 중복을 제거해준다..!
for(int i = 0; i<N; i++){
if(visited[i] == 1) continue;
// 직전의 수와 같은지를 확인해주고
// 같다면 중복이므로 배제해준다.
// 각 재귀마다 새로 before을 선언하고, 따로 이전의 값을 저장하기 때문에
// 같은 값들은 허용해주고, 불필요한 중복만 걸러준다.
if(arr[i] != before){
before = arr[i];
picked[cur] = arr[i];
visited[i] = 1;
recur(cur + 1);
visited[i] = 0;
}
}
}
}
결과는 성공 !
before 값 하나만 추가함으로써 큰 중복을 제거할 수 있다는 점을 새로 깨달았다 ..
다른 문제를 풀이할 때에는 스스로 깨우칠 수 있으면 좋겠다.
오늘도 하나 새로 배워가며 ,,

궁금한 점이 있거나 보충할 점이 있다면 언제든 댓글 부탁드립니다 ~
https://www.acmicpc.net/problem/15663
728x90