본문 바로가기
코딩테스트/프로그래머스Programmers

[프로그래머스][Lv.2][JAVA]기능개발

by 빔o0O 2025. 3. 4.

 

문제

로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.

또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고, 이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.

먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요.

 

제한 사항

  • 작업의 개수(progresses, speeds배열의 길이)는 100개 이하입니다.
  • 작업 진도는 100 미만의 자연수입니다.
  • 작업 속도는 100 이하의 자연수입니다.
  • 배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다. 예를 들어 진도율이 95%인 작업의 개발 속도가 하루에 4%라면 배포는 2일 뒤에 이루어집니다.

입출력 예

 

입출력 예 설명

입출력 예 #1
첫 번째 기능은 93% 완료되어 있고 하루에 1%씩 작업이 가능하므로 7일간 작업 후 배포가 가능합니다.
두 번째 기능은 30%가 완료되어 있고 하루에 30%씩 작업이 가능하므로 3일간 작업 후 배포가 가능합니다. 하지만 이전 첫 번째 기능이 아직 완성된 상태가 아니기 때문에 첫 번째 기능이 배포되는 7일째 배포됩니다.
세 번째 기능은 55%가 완료되어 있고 하루에 5%씩 작업이 가능하므로 9일간 작업 후 배포가 가능합니다.

따라서 7일째에 2개의 기능, 9일째에 1개의 기능이 배포됩니다.

입출력 예 #2
모든 기능이 하루에 1%씩 작업이 가능하므로, 작업이 끝나기까지 남은 일수는 각각 5일, 10일, 1일, 1일, 20일, 1일입니다. 어떤 기능이 먼저 완성되었더라도 앞에 있는 모든 기능이 완성되지 않으면 배포가 불가능합니다.

따라서 5일째에 1개의 기능, 10일째에 3개의 기능, 20일째에 2개의 기능이 배포됩니다.

 


 

분석

  1. progresses[i] 의 진도는 하루가 지날 때마다 speeds[i] 만큼, 그리고 최대 100까지 증가한다. 
  2. 진도가 100이 된 작업 progresses[i]가 있더라도 progress[j] (i > j) 가 배포되지 않았다면 그 작업은 배포되지 않는다. 
    그리고 i보다 앞선 작업들이 배포되는 시점에 같이 배포된다. 
    => progresses[i] 의 진도가 100이 되었을 때, i보다 작은 n에 대해 progresses[n] 이 모두 배포되었는지를 확인하고, 배포되지 않았다면 i 를 배포하지 않아야 한다.
  3. 반대로, progresses[i]가 배포될 때 i보다 큰 m에 대해 progresses[m]이 진도가 100이지만 배포되지 못 한 건이 있다면 같이 배포해야 한다. 
  4. i번째 기능에 대해
    - 배포가능 진도의 플래그 : progresses[i] == 0 
    - 선기능이 모두 개발되어 배포 가능함의 플래그 : progresses[0] + progresses[1] + ... + progresses[i-1] == 100*i
    - 배포완료의 플래그 : (코드로직에 삽입해야함) speeds[i] == 0

 

// 의사코드 작성

class Solution {
    public int[] solution(int[] progresses, int[] speeds) {
        int[] answer = {};
        int todo = progresses.length ;
        List cntList = new ArrayList();		// 리턴값을 위한 변수
        
        // 모든 기능이 개발/배포될 때까지
        // loop 1번 == 1일 
        while(모든 기능이 배포되었나? == false){
        	boolean prevProcFlag = true;	// 선개발 건 배포여부
            int cnt = 0;
        	for(int i = 0; i < progresses.length; i++){
            	// 1. 진도율 증가
            	if(progresses[i] < 100){
                	progresses[i] = progresses[i] + speeds[i] > 100 ? 
                    					100 : progresses[i] + speeds[i];
                }
                
                // 2. 배포가능여부 확인
                // 선개발건 배포됨 && 개발진도 100 && 반영된 적 없음
                if(prevProcFlag && progresses[i] == 100 && speeds[i] != 0){
                	speeds[i] = 0;	// 반영되었음 플래그로 변경
                    cnt++;
                }else{
                	prevProcFlag = false;
                }
            }
            
            if(cnt > 0)	cntList.put(cnt);
        }
        
        return cntList.int배열로변경;
    }
}

 

 


 

피드백

 

🤖 챗지에게 물어봤어요

더보기

작성한 코드에서 몇 가지 개선할 점이 있어요. 주로 효율성가독성을 고려하여 개선할 수 있습니다. 몇 가지 주요 포인트를 첨삭해 보겠습니다.

문제점

  1. 진도율 업데이트 방식: progresses[i] = progresses[i] + speeds[i] > 100 ? 100 : progresses[i] + speeds[i]; 이 코드에서 progresses[i]가 계속해서 100을 초과하지 않도록 처리하려면 매번 100을 비교하는 것이 비효율적입니다. 이를 개선할 수 있습니다.
  2. 불필요한 prevProcFlag 확인: 매번 진도를 체크할 때 prevProcFlag를 매번 확인하고 있는데, 그 값이 매번 반복문 안에서 갱신되기 때문에 조금 더 효율적으로 개선할 수 있습니다.
  3. 배포 가능 여부 판단을 한번에 처리: 각 작업의 배포 가능 여부를 한 번에 처리할 수 있습니다.
  4. cntList 사용 최적화: 매번 리스트로 변환하고 스트림을 사용해 배열로 변환하는 것보다는 미리 배열 크기를 정하고 그 값을 반환하는 방식이 성능상 유리할 수 있습니다.

개선된 코드:

import java.util.*;

class Solution {
    public int[] solution(int[] progresses, int[] speeds) {
        // 완료된 개발 항목을 카운팅할 배열
        List<Integer> cntList = new ArrayList<>();
        
        // 큐를 사용하여 배포할 작업을 처리
        Queue<Integer> progressQueue = new LinkedList<>();
        
        // 각 작업의 개발이 완료되는 날짜 계산
        for (int i = 0; i < progresses.length; i++) {
            // 배포 날짜 계산 (올림처리)
            int remaining = 100 - progresses[i];
            int daysToComplete = (int) Math.ceil(remaining / (double) speeds[i]);
            progressQueue.add(daysToComplete);
        }
        
        // 배포 날짜에 따른 작업 그룹핑
        while (!progressQueue.isEmpty()) {
            int first = progressQueue.poll();
            int cnt = 1; // 첫 번째 배포는 최소 1개

            // 같은 날짜에 배포 가능한 작업들을 카운트
            while (!progressQueue.isEmpty() && progressQueue.peek() <= first) {
                progressQueue.poll();
                cnt++;
            }

            // 그 날짜에 배포 가능한 작업들을 결과 리스트에 추가
            cntList.add(cnt);
        }

        // 결과 리스트를 배열로 변환하여 반환
        return cntList.stream().mapToInt(i -> i).toArray();
    }
}

 

  1. 챗지의 피드백... 너무 날카롭군. 큐를 이용할 생각은 사실 전혀 못 했는데. 피드백을 보면서 큐면 선입후출이라 안 맞지 않나? 했는데 헷갈렸다. 선입후출은 스택stack이고, 큐는 선입선출이다. 어쩐지 내코드 너무 오래 걸리더라... 
  2. 다른 사람의 풀이를 보고 또 벽을 느껴버리는. 아니 난 왜 저생각을 못 했지!!!!! 근데 저걸 안 봤으면 진짜 생각 못 했을 것 같음. 정진하자는 생각만 500일째

 

 


 

답안

import java.util.*;

class Solution {
    public int[] solution(int[] progresses, int[] speeds) {
		int[] answer = {};
		int todo = progresses.length; // 반영해야 할 개발건 개수
		List<Integer> cntList = new ArrayList(); // 리턴값을 위한 변수

		// 모든 기능이 개발/배포될 때까지
		// loop 1번 == 1일
		while (todo > 0) {
			boolean prevProcFlag = true; // 선개발 건 배포여부
			int cnt = 0; // 이번 반영 가능 개수
			for (int i = 0; i < progresses.length; i++) {
				// 1. 진도율 증가
				if (progresses[i] < 100) {
					progresses[i] = progresses[i] + speeds[i] > 100 ? 100 : progresses[i] + speeds[i];
				}
                
				// 2. 배포가능여부 확인
				// 진도 100이 됨 && 배포된 적 없음 && 앞개발건 배포됨
				if (progresses[i] == 100 && speeds[i] != 0 && prevProcFlag) {
					speeds[i] = 0; // 반영되었음 플래그로 변경
					cnt++;
				// 배포된 건도 아닌 경우
				} else if (speeds[i] != 0) {
					prevProcFlag = false;
				}
			}

			if (cnt > 0) {
				cntList.add(cnt);
				todo -= cnt;
			}
		}

		return cntList.stream().mapToInt(i -> i).toArray();
	}
}

 

 

 

 

 


 

다른 사람의 답안

// 이런 식으로도 생각할 수 있구나...
// 주석은 내가 분석하면서 달았음
// 일반적으로 쓰이는 변수들을 쓰면서 그냥 이렇게 간단하게~~~


import java.util.ArrayList;
import java.util.Arrays;
class Solution {
    public int[] solution(int[] progresses, int[] speeds) {
    	// return할때 람다로 0이 아닌 값만 반환할 것이므로 progresses의 최대길이인 100으로 길이 설정
        // 인덱스+1이 일수를 뜻함. 즉 인덱스 0은 1일, 1은 2일, ....
        // 그 인덱스의 value는 그 날 배포할 수 있는 기능의 수
        int[] dayOfend = new int[100];
        int day = -1;		// 일수
        for(int i=0; i<progresses.length; i++) {
        	// 현재 진도율 + (일수*일별진도율)이 100이 안 되면 계속 일수를 증가시킴
            while(progresses[i] + (day*speeds[i]) < 100) {	
                day++;
            }
            
            // 해당 일에 배포되는 작업의 수를 1 증가
            dayOfend[day]++;
        }
        
        // 스트림으로 0이 아닌 값만 필터링하고 그걸 int[]로 변환하여 보낸다
        return Arrays.stream(dayOfend).filter(i -> i!=0).toArray();
    }
}

 

 

 

 

 

 


 

🔗링크

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr