[프로그래머스] 최솟값 만들기 (java) Max_Min

2020-01-13

최솟값 만들기 (programers > lev2 > Max_Min)

문제 링크

문제설명

길이가 같은 배열 A, B 두개가 있습니다. 각 배열은 자연수로 이루어져 있습니다. 배열 A, B에서 각각 한 개의 숫자를 뽑아 두 수를 곱합니다. 이러한 과정을 배열의 길이만큼 반복하며, 두 수를 곱한 값을 누적하여 더합니다. 이때 최종적으로 누적된 값이 최소가 되도록 만드는 것이 목표입니다. (단, 각 배열에서 k번째 숫자를 뽑았다면 다음에 k번째 숫자는 다시 뽑을 수 없습니다.)

예를 들어 A = [1, 4, 2] , B = [5, 4, 4] 라면

  • A에서 첫번째 숫자인 1, B에서 두번째 숫자인 5를 뽑아 곱하여 더합니다. (누적된 값 : 0 + 5(1x5) = 5)
  • A에서 두번째 숫자인 4, B에서 세번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 5 + 16(4x4) = 21)
  • A에서 세번째 숫자인 2, B에서 첫번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 21 + 8(2x4) = 29)

즉, 이 경우가 최소가 되므로 29를 return 합니다.

배열 A, B가 주어질 때 최종적으로 누적된 최솟값을 return 하는 solution 함수를 완성해 주세요.

제한사항
  • 배열 A, B의 크기 : 1,000 이하의 자연수
  • 배열 A, B의 원소의 크기 : 1,000 이하의 자연수
입출력 예
A B answer
[1, 4, 2] [5, 4, 4] 29
[1,2] [3,4] 10
입출력 예 설명

입출력 예 #1 문제의 예시와 같습니다.

입출력 예 #2 A에서 첫번째 숫자인 1, B에서 두번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 4) 다음, A에서 두번째 숫자인 2, B에서 첫번째 숫자인 3을 뽑아 곱하여 더합니다. (누적된 값 : 4 + 6 = 10) 이 경우가 최소이므로 10을 return 합니다.


풀이

import java.util.*;
import java.util.stream.Collectors;

public class prog_2_최솟값만들기 {

    static public int solution(int []A, int []B)
    {
        int answer = 0;
        
        Arrays.sort(A); // 작은 -> 큰 순으로 정렬
        Arrays.sort(B);

        for(int i=0;i<A.length;i++) {
        	answer = answer + (A[i] * B[B.length-i-1]);
        }
        
        return answer;
    }
    
    public static void main(String[] args) {
		int a[] = {1,4,2};
		int b[] = {5,2,8,0};
		solution(a,b);
    	
	}
	
}


후기

처음에는 메모이제이션을 사용하거나, 식을 세워서 푸는 문제라고 생각했다.

하지만 수학적으로 생각해보면 간단한 문제였다.

  1. 각 한번씩만 사용되면서
  2. 누적된 값이 최소가 되게 하려면
  3. A 배열의 제일 작은 값과 B 배열의 제일 큰값을 곱해주면 된다.



tip

  1. 처음에는 B를 list로 변환해 내림차순으로 정렬하고 배열로 바꿔주려고 했다.(쓸데없이…)
  2. Arrays.asList()의 사용은 non primitive (ex, String)만 가능하고 primitive는 불가능하다.
  3. 굳이 primitive (ex, int)형의 배열을 list로 바꾸고 싶은경우에는 아래 처럼 사용한다. (이때, import java.util.stream.Collectors;)
  4. List의 reverse 함수는 내림차순 정렬이 아닌 list의 원소들의 순서만 거꾸로 바꿀 뿐이다. (정렬 X)
  5. 보통의 sort 함수는 오름차순이고, list를 내림차순으로 사용하고싶다면 아래의 함수를 사용한다.
  6. list를 배열로 바꾸는 법도 까먹지 않도록 한다!
     2. Arrays.asList()
         
     3. List<Integer> list = Arrays.stream(B).boxed().collect(Collectors.toList());             -> 필수! import java.util.stream.Collectors;

     4. Collections.reverse(list);

     5. LinkedList<Integer> list = new LinkedList<Integer>();
        Collections.sort(list,Collections.reverseOrder());
 
     6. list.stream().mapToInt(k->k).toArray();
       

comparator 와 comparable의 차이점 설명하는 블로그


Read More

[프로그래머스] 라면공장 (java) Heap

2020-01-08

라면공장 (programers > lev2 > Stack_Queue)

문제 링크

라면 공장에서는 하루에 밀가루를 1톤씩 사용합니다. 원래 밀가루를 공급받던 공장의 고장으로 앞으로 k일 이후에야 밀가루를 공급받을 수 있기 때문에 해외 공장에서 밀가루를 수입해야 합니다.

해외 공장에서는 향후 밀가루를 공급할 수 있는 날짜와 수량을 알려주었고, 라면 공장에서는 운송비를 줄이기 위해 최소한의 횟수로 밀가루를 공급받고 싶습니다.

현재 공장에 남아있는 밀가루 수량 stock, 밀가루 공급 일정(dates)과 해당 시점에 공급 가능한 밀가루 수량(supplies), 원래 공장으로부터 공급받을 수 있는 시점 k가 주어질 때, 밀가루가 떨어지지 않고 공장을 운영하기 위해서 최소한 몇 번 해외 공장으로부터 밀가루를 공급받아야 하는지를 return 하도록 solution 함수를 완성하세요.

dates[i]에는 i번째 공급 가능일이 들어있으며, supplies[i]에는 dates[i] 날짜에 공급 가능한 밀가루 수량이 들어 있습니다.

제한사항
  • stock에 있는 밀가루는 오늘(0일 이후)부터 사용됩니다.
  • stock과 k는 2 이상 100,000 이하입니다.
  • dates의 각 원소는 1 이상 k 이하입니다.
  • supplies의 각 원소는 1 이상 1,000 이하입니다.
  • dates와 supplies의 길이는 1 이상 20,000 이하입니다.
  • k일 째에는 밀가루가 충분히 공급되기 때문에 k-1일에 사용할 수량까지만 확보하면 됩니다.
  • dates에 들어있는 날짜는 오름차순 정렬되어 있습니다.
  • dates에 들어있는 날짜에 공급되는 밀가루는 작업 시작 전 새벽에 공급되는 것을 기준으로 합니다. 예를 들어 9일째에 밀가루가 바닥나더라도, 10일째에 공급받으면 10일째에는 공장을 운영할 수 있습니다.
  • 밀가루가 바닥나는 경우는 주어지지 않습니다.
입출력 예
stock dates supplies k result
4 [4,10,15] [20,5,10] 30 2
입출력 예 설명
  • 현재 밀가루가 4톤 남아 있기 때문에 오늘과 1일 후~3일 후까지 사용하고 나면 모든 밀가루를 다 사용합니다. 따라서 4일 후에는 반드시 밀가루를 공급받아야 합니다.
  • 4일째 공급받고 나면 15일 이후 아침에는 9톤의 밀가루가 남아있게 되고, 이때 10톤을 더 공급받으면 19톤이 남아있게 됩니다. 15일 이후부터 29일 이후까지 필요한 밀가루는 15톤이므로 더 이상의 공급은 필요 없습니다.
  • 따라서 총 2회의 밀가루를 공급받으면 됩니다.


풀이

    public static int solution(int stock, int[] dates, int[] supplies, int k) {
        int answer = 0;
        
        //원래는 오름차순인 pq를 내림차순으로 바꿔준다.
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>(Comparator.reverseOrder());
        
        int idx = 0; //dates의 개수만큼만 작동하게 한다. i를 활용하면 k-1까지 가니까 다른 변수 사용해야함
        
        for(int i=0;i<k;i++) {//i는 하루하루 지나는 날짜, k-1만큼만 돈다.
        	//아 들어가지않아도 dates[3]을 검사하면서 이미 error!!!!
        	if(idx < dates.length &&i == dates[idx]){//dates는 정렬되어있으므로 dates[0], dates[1], dates[2]..
        		pq.offer(supplies[idx]); // 같은 위치에 있는 supplies[0], supplies[1],,,을 pq에 넣고
        		idx++;//다음 data를 위해 idx는 증가시킨다.
        	}
        	
        	if(stock == 0) {//0이 되면 새로운 pq의 새로운 supplies를 충전해야하고 이때 맨앞에있는 값이 제일 크므로 stock에 더해줌
        		stock = stock+ pq.poll();
        		answer++; //pq에서 빠질 때마다 해외에서 공급해오는 것이기때문에 answer+1
        	}
        	
        	//for문 한번 돌때마다 무조건 stock은 -1
        	stock = stock-1;
        }
        
        
        return answer;
    }
    
	public static void main(String[] args) {
		int dates[] = {4,10,15};
		int supplies[] = {20,5,10};
		
		System.out.println(solution(4,dates,supplies,30));
	}


후기

Heap은 우선순위 큐에 사용되는 자료구조이고 java에서는 PriorityQueue로 구현할 수 있다. 루트에 제일 작은 원소가 들어가는 min heap과 루트에 제일 큰 값이 들어가는 max heap으로 되어있고, 구현되어있는 PQ는 오름차순인 min heap으로 되어있다.

처음엔 supplies와 dates가 함께 움직여야한다고 생각했다. supplies의 값들이 내림차순으로 정렬되고 dates도 함께 움직여야 한다고 생각했지만 dates가 순서대로 정렬되어있기 때문에 dates에 따른 supplies값을 PQ에 넣고 그중 제일 큰 값만 Stock==0 일 때 꺼내주면 문제는 쉽게 해결 되었다.

문제를 이해하는데 생각보다 오래 걸렸지만 한 번 이해하고 나면 쉽게 풀 수 있었고 문제 속에 힌트가 있었다.



tip

  1. pq에 supplies에 넣어준다.
  2. Java에서 Heap(PriorityQueue)는 오름차순으로 되어있고 내림차순으로 사용하기 위해서는 Comparator를 사용해야한다.
  3. idx < dates.length 필수! if문에 걸리고 dates[idx++] IndexBoundError가 생긴다! 조심!
  4. i는 날짜로 사용한다! (for문 한 번이면 해결된다)


Read More

[프로그래머스] 프린터 (java) Queue

2020-01-07

프린터 (programers > lev2 > Stack_Queue)

package Stack_Queue;

import java.util.*;

public class prog_2_프린터 {

    static public int solution(int[] priorities, int location) {
        int answer = 0;

        //Queue는 내부를 다 훑어보지 못하므로 LinkedList 사용
        LinkedList<Integer> index = new LinkedList<Integer>(); //index
        LinkedList<Integer> prio = new LinkedList<Integer>(); // 중요도
        boolean check = false;

        for(int i=0;i<priorities.length;i++) {
        	index.add(i);
        	prio.add(priorities[i]);
        }

        while(true) {
        	for(int i=1;i<prio.size();i++) {//맨앞과, 나머지 비교
        		if(prio.get(0)<prio.get(i)) {
        			//맨앞보다 중요도 높은 것들이 있다면 제거해서 뒤로 보내준다
        			int rem_idx = index.remove(0);
        			index.add(rem_idx);

        			int rem_prio = prio.remove(0);
        			prio.add(rem_prio);

        			check= true;
        		}
        	}

        	if(check == false) { //나보다 큰애가 없다는거
        		if(index.get(0) == location) {
        			answer++;
        			break;
        		}

        		else {
        			index.remove(0);
        			prio.remove(0);
        			answer++;
        		}
        	}

        	check=false; // 빼먹지 않도록 주의한다! 자칫하면 무한루프 돌 수 있음
        }


        return answer;
    }

	public static void main(String[] args) {
		int arr[] = {2,1,3,2};
		System.out.println(solution(arr,2));
	}
}

  • 마지막에 check=false;를 빼먹어서 무한루프 돌았다. 빼먹지 않도록! (30min)


+1/8

    class Attribute{
    	int index;
    	int prio;
    	
    	Attribute(int index, int prio){
    		this.index = index;
    		this.prio = prio;
    	}

    }
    
    public int sol(int[] priorities, int location) {
    	int answer = 0;
    	
    	LinkedList<Attribute> att = new LinkedList<Attribute>();
    	boolean check = false;
    	
    	for(int i=0;i<priorities.length;i++)
    		att.add(new Attribute(i, priorities[i]));
 
    	while(true) {
    		for(int i=1;i<att.size();i++) {
    			if(att.get(0).prio < att.get(i).prio) {
    				check = true;
    				Attribute back = att.remove();
    				att.add(back);
    				break;
    			}
    		}//for
    		
    		if(check == false) {
    			answer++;
    			
    			if(att.get(0).index == location)
    				break;
    			
    			att.remove();
    		}
    		else
    			check = false;
    	}
    	
    	return answer;
    }

+동시에 list두 개를 움직이는게 보기 좋지 않아서 class를 만들어서 list에 담았다. 그나마 쪼끔 깔끔해졌다!


Read More

[프로그래머스] 탑 (java) Queue

2020-01-07

탑 (programers > lev2 > Stack_Queue)

package programmers;

import java.util.*;

//스택, 큐
public class prog_2_ {

	static public int[] solution(int[] heights) {
		int size = heights.length;
		int arr[] = new int[size+1]; // index 헷갈리지 않기 위해서 size+1 만큼 만듦
		boolean check = false;

		ArrayList<Integer> list  = new ArrayList<Integer>();
		list.add(0);

		for(int i=0;i<size;i++)
			arr[i+1] = heights[i];

		for(int i=2; i<=size; i++) { //1층은 0이니까 2층부터 확인
			for(int j=i-1; j>=1; j--) { // i-1층부터 차례차례 밑 확인
				if(arr[i] <arr[j]) { //현재층보다 큰게 있다면
					list.add(j); //답에 넣어주고
					check = true;
					break;
				}
			}

			if(check== false) //걸리는거 하나 없이 나온다면-> 큰게 없다는거
				list.add(0); //그럼 답에 0 추가

			check = false; // check에 true걸려있을 수도 있으니까 false로 처리
		}

		return list.stream().mapToInt(k->k).toArray(); // list to Array

	}


	public static void main(String[] args) {

	}

}

  • list to array 방법에 대해서 외워두자!

  • 여담인데 블로그에 yaml이 안먹혀서 뭔가 이십분 헤맸는데 title에 대괄호를 쓸때는 ““를 붙여줘야한다는 사실을 밑에 블로그에서 알게됨 꺅! https://simhyejin.github.io/2016/06/24/markdown-post-title/

Read More

Recursion1 (재귀 기초)

2020-01-06

1. 문자열 재귀로 하나씩 출력하기

public class Solution {

    static void ja(String a) {
	if(a.length() == 0)
		return ;

	else {
		System.out.println(a.charAt(0));
		ja(a.substring(1));
	}

}

    public static void main(String[] args) {
        ja("hello world!");
    }
}



2. 문자열 반대로 출력하기

public class Solution {


	static void ja(String a) {
		if(a.length() == 0)
			return ;

		else {
			System.out.println(a.charAt(a.length()-1));
			ja(a.substring(0, a.length()-1));
		}

	}


	public static void main(String[] args) {
		ja("hello world!");
	}

}

* 기저조건에서 return 0; 으로 끝내기때문에 돌아가지않고 ->->->안으로 따라들어가고 끝남 (for문과 비슷하게 작동)



3. 최대공약수 구하기 (by 유클리드호제법)

	static int sol(int m, int n) {
		// m >= n 라고 가정
		if(m%n == 0)
			return n; //나뉘어 떨어지면 n의 배수가 m이라는 얘기니까 n자체가 최대공약수
		else
			return sol(n, m%n); // ->->->들어갔다가 if에 걸린 n값이 return으로 돌아옴

	}


	public static void main(String[] args) {
		System.out.println(sol(15,10));
	}



  • 유클리드 호제법

    m>=n 일때 (m,n)의 최대공약수는 (n,m%n)과 같다.

  • 기저조건이 if문이고 아무리 큰 수도 else를 타고 가다보면 if를 만나고

    1) 그전에 배수가 되는걸 만나면 n return

    2)나누다보면 나머지는 제일 작은 1이 되기때문에 1 return

    -> 결국은 기저조건에서 만나는 n or 1이라는 같은 값을 계속 올라오면서 return




4. 2진수 출력하기

public class Solution {

	static void sol(int n) {
		if(n == 1) { //기저조건: 몫이 1이되는 순간

			System.out.println(1);
		}
		else {
			sol(n/2); // 몫을 타고 들어간다. when? 몫==1 일때까지
			System.out.println(n%2);//기저에서부터 올라오면서 출력해야하니까
		}
	}


	public static void main(String[] args) {
		sol(10); // 나머지,나머지,나머지... 몫
	}

}
  • 그럼 8진법은? n/8 과 n%8



Read More