회사원 Demi는 가끔은 야근을 하는데요, 야근을 하면 야근 피로도가 쌓입니다. 야근 피로도는 야근을 시작한 시점에서 남은 일의 작업량을 제곱하여 더한 값입니다. Demi는 N시간 동안 야근 피로도를 최소화하도록 일할 겁니다.Demi가 1시간 동안 작업량 1만큼을 처리할 수 있다고 할 때, 퇴근까지 남은 N 시간과 각 일에 대한 작업량 works에 대해 야근 피로도를 최소화한 값을 리턴하는 함수 solution을 완성해주세요.
제한 사항
works는 길이 1 이상, 20,000 이하인 배열입니다.
works의 원소는 50000 이하인 자연수입니다.
n은 1,000,000 이하인 자연수입니다.
입출력 예worksnresult
[4, 3, 3]
4
12
[2, 1, 2]
1
6
[1,1]
3
0
입출력 예 설명
입출력 예 #1 n=4 일 때, 남은 일의 작업량이 [4, 3, 3] 이라면 야근 지수를 최소화하기 위해 4시간동안 일을 한 결과는 [2, 2, 2]입니다. 이 때 야근 지수는 22+ 22+ 22= 12 입니다.
입출력 예 #2 n=1일 때, 남은 일의 작업량이 [2,1,2]라면 야근 지수를 최소화하기 위해 1시간동안 일을 한 결과는 [1,1,2]입니다. 야근지수는 12+ 12+ 22= 6입니다.
입출력 예 #3
남은 작업량이 없으므로 피로도는 0입니다.
📝풀이
우선순위 큐를 이용하여 문제 해결
import java.util.*;
class Solution {
public long solution(int n, int[] works) {
long answer = 0;
PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());
for(int i=0; i<works.length; i++){
pq.offer(works[i]);
}
int max;
while(n>0){
if(pq.peek()==0)
return 0;
pq.offer(pq.poll()-1);
n--;
}
while(!pq.isEmpty()){
answer+= Math.pow(pq.poll(), 2);
}
return answer;
}
}
네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있을 때 컴퓨터 A와 컴퓨터 C도 간접적으로 연결되어 정보를 교환할 수 있습니다. 따라서 컴퓨터 A, B, C는 모두 같은 네트워크 상에 있다고 할 수 있습니다.
컴퓨터의 개수 n, 연결에 대한 정보가 담긴 2차원 배열 computers가 매개변수로 주어질 때, 네트워크의 개수를 return 하도록 solution 함수를 작성하시오.
제한사항
컴퓨터의 개수 n은 1 이상 200 이하인 자연수입니다.
각 컴퓨터는 0부터n-1인 정수로 표현합니다.
i번 컴퓨터와 j번 컴퓨터가 연결되어 있으면 computers[i][j]를 1로 표현합니다.
computer[i][i]는 항상 1입니다.
[입출력 예]
n
computers
return
3
[[1, 1, 0], [1, 1, 0], [0, 0, 1]]
2
3
[[1, 1, 0], [1, 1, 1], [0, 1, 1]]
1
입출력 예 설명
예제 #1 아래와 같이 2개의 네트워크가 있습니다.
예제 #2 아래와 같이 1개의 네트워크가 있습니다.
📝풀이
bfs 풀이
import java.util.*;
class Solution {
static boolean[] visit;
static Queue<Integer> queue= new LinkedList<Integer>();
public int solution(int n, int[][] computers) {
int answer = 0;
visit = new boolean[n];
for(int i=0; i<n; i++){
if(visit[i]) {
continue;
}
answer++;
bfs(computers, i, n);
}
return answer;
}
public void bfs(int[][] arr, int start, int len){
int x;
queue.offer(start);
while(!queue.isEmpty()){
x= queue.poll();
for(int y=0; y<len; y++){
if((arr[y][x]==1 || arr[x][y]==1) && !visit[y]){
visit[y]= true;
queue.offer(y);
}
}
}
}
}
반례
dfs / bfs 풀 때 보통 input 배열 기준으로 상하좌우로 움직이다보니, 처음에 이 문제를 잘못 이해했었는데요.
'프로그래머스 질문하기' 에서 어떤 분이 알려주신 반례로 문제를 다시 생각해보니 올바르게 이해가 되었습니다.
- 필드(인스턴스 변수)의 수가 많은 것은 객체의 복잡도를 높이고, 버그 발생 가능성을 높일 수 있다.
- 중복이 있거나, 불필요한 필드가 없는지 확인해 필드의 수를 최소화 한다.
6. 비즈니스 로직과 UI 로직을 분리해라
- 비즈니스 로직과 UI로직을 한 클래스가 담당하지 않도록 한다. 단일 책임의 원칙에도 위배된다.
- 현재 객체의 상태를 보기 위한 로그 메시지 성격이 강하다면 toString()을 통해 구현한다.
- View에서 사용할 데이터라면 getter메서드를 통해 데이터 전달
3주차 회고
2주차 보다 고려해야될게 훨씬 많았던 미션이었습니다.
input 형식이 달라져서 입력값을 처리해야 하는 부분도 생겼고, 지난 주 보다 고려해야할 예외상황도 많았습니다.
따라서 관련 함수와 클래스 추가로 구조도 좀 더 복잡해졌습니다. 그래서 구조 설계부터 "비즈니스 로직과 UI로직을 분리하라"고 했던 피드백을 다시 떠올리며 시작했습니다.
구현 중에는 필드 수를 줄이라고 했던 피드백을 신경쓰면서 구현하려고 했고, 중복되는 부분도 제거해보려고 노력 하였습니다.
지난주에 나름 구조에 대한 틀을 잡았다고 생각했지만, 조금 더 복잡해졌다고 다시 다른 고민들이 많이 생기더라고요.
* 구조
- validation코드를 어디에 넣어야하는지, 폴더를 따로 구분해서 두는게 좋을지 아니면 객체를 역할에 따라 좀더 세분화 해서 그 안에 넣어야 하는지
- Controller랑 Service랑 다른 역할인 것 같은데 Service만 모아놓는 폴더를 따로 만들어야 하는지 아니면 Controller랑 같이 둬도 되는지
- DTO? VO? 다른 분들 코드에서 나눈 분들도 있고, 안 나눈 분들도 있는데 이번엔 나누지 않았지만, 코드가 더 복잡해질 경우 객체 관리를 위해 나누는 게 좋을 것 같다는 생각.
* Stream
- stream을 사용하면서 코드를 더 간결하게 사용할 수 있었음. 하지만 아직 학습이 부족해서 응용이 어려움. stream에 대해 더 공부해볼 것(filter, map, sort 등등)
* 테스트
- 코드를 구현할 때 객체를 먼저 어느정도 만들고, 필요한 상수를 담아놓는 클래스를 어느정도 만들어 놓고, 서비스 관련 로직을 구현한다. 그런데 프로그램은 결국 main에서 controller를 만들어 실행시키게 되는데, 중간에 테스트가 필요한 경우 어떤식으로 해야할지 고민... (단위 테스트 하는 방법)
* enum
- 사용해본 적이 없어서 조금 헤맸었던 부분
- (최종 코테를 보고나니 더욱이 드는 생각,,) 어떤 상황에 쓰는지 어떻게 활용할 수 있는지 더 공부해봐야할 것 같습니다.
3주동안 프리코스를 진행해보면서 내가 잘하고 있다고 생각했던 부분은 다시 돌아볼 수 있었고, 협업에서 지켜야할 부분들에 대해서는 새로 배울 수 있었습니다. 개인적으로는 저의 나쁜 습관을 고치고(주석, 변수 이름 짓는 부분 etc) 좀 더 효율적인 코드를 짤 수 있도록 개선한 것에 대해 정말 의미를 두고 싶네요. 1:1 피드백은 없었지만, 공통 피드백도 정말 도움이 많이 되었던 것 같습니다. 최종 코딩 테스트는 많이 아쉬움이 남았지만, 재밌었던 프리코스 였습니다.
- 시도 횟수만큼의 라운드가 끝난 후에 최종 우승자를 출력합니다. (2명 이상일 경우에는 ,쉼표로 구분하여 출력)
4. 기타 사항
[Exception]
- 사용자의 입력이 잘못되었을 경우에 IllegalException을 발생시키며, 에러메시지를 출력하고 다시 입력받습니다.
[구현]
- 자바 코드 컨벤션을 지킨다.
- indent는 2이하
- else 예약어를 사용하지 않는다.
- 함수는 한가지 기능만 하게 구현
- 프로그래밍 요구사항에서 별도로 변경 불가 안내가 없는 경우, 파일 수정과 패키지 이동을 자유롭게 할 수 있다.
1주차 피드백
1주차 공통 피드백의 내용 중에 제가 고쳐야할/인상깊었던 피드백에 대해 몇가지만 적어보려고 합니다.
1. 이름을 통해 의도를 드러내고, 이름을 축약하지 마라
- 연속된 숫자를 덧붙이거나 불용어(a, an, the, data, info) 사용하지 말 것
- 의도를 드러낼 수 있다면 이름은 길어져도 괜찮다.
- 클래스 이름이 order이고 함수 이름이 ship이라면 order.ship()을 호출하게 되면서, 이름으로 의도를 충분히 드러낼 수 있다.
:항상 느끼지만 이름 작성하는게 참 어렵다고 생각하는데, 피드백을 통해 정확히 어떤 것을 지양해야하는지 듣고나니 앞으로 좀 더 명확하게 이름을 작성할 수 있도록 노력해야겠다는 생각이 들었습니다.
2. 반복하지 마라
3. 의미없는 주석은 달지 않는다.
- 가능하면 이름을 통해 기능의 의도가 드러난다면 주석을 달지 않는다.
** 제가.. 주석 괴물이었다는 것을 깨닫게 된 피드백...ㅋㅋㅋㅋ
예전 학부 시절.. 주석 안달았다가 감점 당한 적이 있었는데..하하 그 이후로 주석을 달기 시작해서 최근에는 너무 꼼꼼히, 길게 달게 된 것 같아요. 너무 많은 주석을 다는 것도 코드를 볼 때 가독성을 떨어뜨리는 것을 깨달을 수 있었습니다. ( 그래서 "이름을 통해 의도를 드러낸다"는 말이 저에겐 정말 의미있는 피드백 이었던 것 같습니다. )
4. IDE의 코드 자동 정렬 기능을 활용해라
Eclipse: ctrl + shift + F
IntelliJ: ctrl + alt + L
** 이것은 몰랐던 꿀팁..!
앞으로 자주 사용하게 될 것 같습니다~
근데 stream을 사용하게 되면 '.' (마침표) 에 따라 \n했던게 다 한 줄 처리가 되는데 이건 어떻게 안되는지..? 확인해봐야할 것 같아요.
이번주 미션에는 위와 같은 안내 사항이 있었기 때문에, 클래스와 패키지 분리에 신경써보려고 했습니다.
이전에는 이클립스로 거의 알고리즘을 풀기만 했었기 때문에, 클래스나 패키지를 여러개 둘 일이 없었습니다. 그래서 다른 분들의 코드도 참고해보고 MVC패턴도 한번 공부해보면서 적용하려고 노력했습니다.
- MVC패턴 적용 -
기능에 따라 클래스를 나누고 용도에 따라 패키지를 나눠서 사용하려고 했습니다. Model과 View를 완전히 분리하고 controller로 domain과 view를 이용하여 프로그램을 구현하는 것에 목표를 두고 구현했는데...
domain과 view의 분리를 완벽히 시키지 못해서 아쉬웠던 것 같습니다.
domain에서 print안하고 view에 객체/인자 전달해서 출력하도록 해야했는데, 함수 하나를 그렇게 하지 못했습니다. 미션 중에는 차라리 자동차의 현재 진행상태를 문자열로 반환하여 OutputView에서 출력시킬까 했는데, 적용해 볼 걸 그랬나봐요. 2주차 공통 피드백에 이 방법이 쓰여져 있더라고요..!
패턴을 적용하여 프로그램을 구현해본 적이 없기 때문에, 익숙치 않아서 그런지 구현보다는 설계가 어려웠습니다. ( 아직도 어디까지 domain에 함수로 넣을 수 있고/없는지 헷갈리네요.. )
다음주에는 이부분을 더 신경써서 고민해봐야 할 것 같습니다.
2. else 예약어 사용 안하기
if하면 else..! 이렇게 자주 사용했던 예약어를 혹여나 무심코라도 안쓰기 위해 처음에는 의식을 좀 했던 것 같습니다. else를 많이 사용하긴 했지만 그래도 평소에 if() return의 구조 역시 많이 사용했기 때문에, 구현이 어려웠다거나 별다른 문제점이 있지는 않았습니다.
3. 예외처리
사실 그 동안은 예외처리를 약간 뭉뚱그려서 하는 안좋은 습관을.. 가지고 있었습니다...(반성중..;ㅅ;)
근데 이번 주차 미션을 하면서 에러 발생 상황을 생각해보고 적절한 에러메시지를 띄운 후 입력값을 다시 받도록 구현하는 연습을 할 수 있었습니다.
+) 다른 분들의 코드를 구경하다가(?)/ 참고하다가 stream 이라는 것을 보았는데, 생각보다 기능도 많고 유용해보이더라고요. 그래서 조금씩 사용해보면서 익혀보려고 합니다.
이전에 조금이나마 git을 사용했을 때는 커밋 메시지나 커밋을 어떻게 해야하는지 모르고 커밋을 했었습니다. (그냥 통째로 커밋하고 메시지도 제대로 안쓰고,,) 그런데 이번에는 프로그램 자체에서부터 기능별로 나눠서 클래스와 메소드를 구현하고, 커밋도 기능에 따라 하려니 어렵더라고요...
완벽하진 않았지만 그래도 이를 지키면서, 기능을 구현하고 커밋하려고 노력했더니 전보다 코드 가독성이 높아진 것을 느낄 수 있었습니다.
(프리코스가 끝날 때 쯤엔 더 발전해있겠죠..? 화이팅(。・∀・)ノ゙)
3. 커밋 메시지 작성
컨벤션을 지켜서 commit message를 잘 작성하기 위해서는 한 줄짜리 메시지가 아니라 여러줄의 자세한 메시지를 작성할 줄 알아야 했습니다. 그래서 템플릿을 써보려고 했으나, 띄워진 메시지 템플릿에서 편집기가 제대로 작동하지 않아서, 잘못된 커밋 메시지가 저장되었고 이를 수정하려고 amend를 쓰다가 파일이 섞였습니다....
그래서 1주차에는 메시지를 자세히 작성하진 못하고 컨벤션에 따라 head message만 작성했습니다.