ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 16401 과자 나눠주기
    백준/이분탐색 2021. 10. 28. 17:15

     

    https://www.acmicpc.net/problem/16401

     

    16401번: 과자 나눠주기

    첫째 줄에  조카의 수 M (1 ≤ M ≤ 1,000,000), 과자의 수 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 과자 N개의 길이 L1, L2, ..., LN이 공백으로 구분되어 주어진다. 과자의 길이는 (1 ≤ L1, L2, ..., LN 

    www.acmicpc.net

    <최종 코드>

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.StringTokenizer;
     
    public class Main {
    	
    	
    	static int N;
    	static int M;
    	static int snack[];
    	static int length;
    	public static void main(String[] args) throws IOException {
    	
    		
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		
    		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
    		N = Integer.parseInt(st.nextToken());
    		M = Integer.parseInt(st.nextToken());
    		
    	
    		snack = new int [M];
    		StringTokenizer st1 = new StringTokenizer(br.readLine(), " ");
    		
    		for(int i=0; i<M;i++) {
    			snack[i]= Integer.parseInt(st1.nextToken());
    		}
    	
    		Arrays.sort(snack);
    		search(1,snack[M-1]);
    		System.out.println(length);
    	}
    		
    		public static void search(int left, int right) {
    			
    		if(left>right) {
    			return;
    		}
    		
    		int mid=(left+right)/2;
        		int count=0;
    		for(int i=0;i<M;i++) {
    			if(snack[i]>=mid) {
    				int n = snack[i]/mid;
    			count+=n;
    			}
    		}
    		
    		if(count>=N) {
    			if(length<mid) length = mid;				
    			left = mid+1;
    			search(left, right);
    		}else if (count<N) {
    			right= mid-1;
    			search(left, right);
    		}
    	  }
        }

    이분 탐색을 많이 풀어보지 못해서 참고자료 블로그를 참고했다.

     

     

    #1. search 메소드 맨 아래 부분

     

    1) count>=N

    2) count<N 

    인 두가지 경우가 생긴다.

     

    1) count>=N 인 경우

    length= mid 로 length 값을 바꿔주는데,

    count>=N 이라, 조카 1명에게 줄 수 있는 막대 과자 최대 길이를 찾을 수 있기 때문에, length 값을 mid 로 바꿔서 출력하도록 하는 거고,

     

    2) count<N 인 경우

    조카 1명에게 줄 수 있는 막대 과자의 최대 길이를 찾을 수 없기 때문에,

    그냥 length=0 인 초기화 상태가 유지되도록 mid 값으로 바꿔주지 않는 것이다. 

     

     

     

    # 2. static 변수

    원래는 static 변수를 사용하면, 오류 발생 가능성이 크다고 배워서 사용하지 않으려고 했다.

    그러면 search 메소드에 매개변수가 많아진다.

     

    search(int N, int M, int left, int right, int snack[], int length)

    그런데, 이런식으로 매개변수가 너무 많아도 코드가 좋지 않다고 한다.

    그래서 어느 걸 선택해야 좋을지 모르겠다.

     

     

     

     

    참고 자료 :

     

    https://daily-life-of-bsh.tistory.com/189

     

    [백준] 16401. 과자 나눠주기

    https://www.acmicpc.net/problem/16401 16401번: 과자 나눠주기 첫째 줄에  조카의 수 M (1 ≤ M ≤ 1,000,000), 과자의 수 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 과자 N개의 길이 L1, L2, ..., LN이..

    daily-life-of-bsh.tistory.com

     

     

    ----------------------------------------------------------------------(이 부분부터는 읽지 않으셔도 됩니다.)

    원래 나는 N<=M, N>M 인 경우를 나눠서 처리하려고 했다.

    예제에 나온 것을 보면 알 것이다.
    첫번째 예제는 N(3)<=M(10) 이고
    두번째 예제는 N>M이다.

     

    일단 첫번째 예제를 보겠다.

    3 10

    1 2 3 4 5 6 7 8 9 10

     

    N=3, M= 10 인 경우,

    현재 존재하는 과자 수(10) > 필요한 과자 수 (3) 

    이므로

    기존에 주어진 과자 길이(1~10) 중 결과 값을 찾을 수 있다.

    즉, 1~10 중 조카 1명에게 줄 수 있는 막대 과자의 최대 길이를 구할 수 있다.

    예제 출력값이 8인 것만 봐도 알 수 있을 것이다.

    그래서 이 경우는

    left = snack[0]=1,

    right = snack[M-1]=10 

    이렇게 범위를 정해준다.

     

     

    그리고 두 번째 예제를 보겠다.

    4 3

    10 10 15

     

    N=4, M = 3인 경우

    현재 존재하는 과자 수(3)< 필요한 과자 수 (4) 

    이다. 따라서 기존 존재하는 과자에서 길이를 잘라서 찾아야만 한다.

    그래서 여러가지 경우가 존재하는데, 

    10 10 15 중 제일 작은 값인 10을 기준으로

     

    1. 10보다 같거나 작을 경우

    1) 예제 입력이 10 10 15 이고 찾고자 하는 과자 개수가 4라면 정답은 7

    2) 예제 입력이 10 10 20 이고 찾고자 하는 과자 개수가 4라면 정답은 10 

     

    2. 10 보다 클 경우 

    1) 예제 입력이 10 10 100 이고 찾고자 하는 과자 개수가 4라면 25

    에서 답이 나올 수 있다.

     

    때문에 이 경우는 10 10 15 의 범위 내에서 조카 1명에게 줄 수 있는 막대 과자의 최대 길이를 구할 수 있는 게 아니라,

    1~15(제일 길은 과자 길이) 에서 정답을 찾을 수 있다.

    그래서

    left= 1

    right = snack[M-1] =15

    이렇게 범위를 정해줄 수 있다.

     

    즉 첫번째 예제는 

    left = snack[0]

    right = snack[M-1]

     

    두번째 예제는 

    left= 1

    right = snack[M-1]

     

    즉 left의 초기값이 달라질 수 있다.

     

     

    그런 조건을 추가했을때, 아래 코드 부분이다

    ******* 친 부분

     

     

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.StringTokenizer;
     
    public class Main {
    	
    	
    	static int N;
    	static int M;
    	static int snack[];
    	static int length;
    	public static void main(String[] args) throws IOException {
    	
    		
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		
    		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
    		N = Integer.parseInt(st.nextToken());
    		M =   Integer.parseInt(st.nextToken());
    		
    	
    		snack = new int [M];
    		StringTokenizer st1 = new StringTokenizer(br.readLine(), " ");
    		
    		for(int i=0; i<M;i++) {
    			snack[i]= Integer.parseInt(st1.nextToken());
    		}
    	
    		Arrays.sort(snack);
    		
    		//*********************
    		int left=0;
    		int right=0;
    
    		if(N<=M) { 
    			left = snack[0]; 
    			right=snack[M-1];
    			
    		}else if (N>M) { 
    			left = 1;
    			right=snack[M-1]; 
    			
    		}
    		search(left,right);
    		
    		//**********************
    		
    		
    		
    		
    		System.out.println(length);
    	}
    		
    		public static void search(int left, int right) {
    			
    		if(left>right) {
    			return;
    		}
    		
    		int mid=(left+right)/2;
        	int count=0;
    		for(int i=0;i<M;i++) {
    			if(snack[i]>=mid) {
    				int n = snack[i]/mid;
    			count+=n;
    			}
    		}
    		
    		if(count>=N) {
    			if(length<mid) length = mid;				
    			left = mid+1;
    			search(left, right);
    		}else if (count<N) {
    			right= mid-1;
    			search(left, right);
    		}
    		}
    		}

     

     

    이 코드를 추가했을 때 결과가 윗 부분인데, 시간이(1288로 1316보다) 엄청 빠르지도 않다.

    컴퓨터 속도가 빠른데, 내가 굳이 이런 경우까지 나눠서 할 필요가 없었다.

     

    그래서 지움.

    나중에 실력이 오른다면 너무 당연한 얘기겠지만, 이런 것도 기록해두면 나중에 읽어봤을 때 많이 성장했다는 걸 알 것 같아서 기록했다.

     

     

     

     

     

     

     

     

    '백준 > 이분탐색' 카테고리의 다른 글

    20444 색종이와 가위  (0) 2022.06.05
    1561 놀이 공원  (0) 2022.05.28
    13702 이상한 술집  (0) 2022.05.04
    12015 가장 긴 증가하는 부분 수열 2  (0) 2022.02.11
    7795 먹을 것인가 먹힐 것인가 (java)  (0) 2022.02.03

    댓글

Designed by Tistory.