문제 설명

어떤 숫자에서 k개의 수를 제거했을 때 얻을 수 있는 가장 큰 숫자를 구하려 합니다.

예를 들어, 숫자 1924에서 수 두 개를 제거하면 [19, 12, 14, 92, 94, 24] 를 만들 수 있습니다. 이 중 가장 큰 숫자는 94 입니다.

문자열 형식으로 숫자 number와 제거할 수의 개수 k가 solution 함수의 매개변수로 주어집니다. number에서 k 개의 수를 제거했을 때 만들 수 있는 수 중 가장 큰 숫자를 문자열 형태로 return 하도록 solution 함수를 완성하세요.

 

제한 조건

  • number는 1자리 이상, 1,000,000자리 이하인 숫자입니다.
  • k는 1 이상 number의 자릿수 미만인 자연수입니다.

나의 풀이

 탐욕법문제. 나의 접근은 주어지는 숫자에서 몇 개의 수만 빼고 최대 값이니, 일단 자리수가 고정된다는 데서 시작했다. 주어진 문자에서 첫번째 자리에 올 수 있는 수자는 (주어진 문자 - k +1) 이기 때문에 그 중 가장 큰 숫자를 고른다. 만약 가장 큰 수와 같은 수가 있다면 젤 앞쪽에 오는 수를 선택한다. 그리고 다시 다음자리에 올 수 있는 범위를 정하고, 그중에 가장 큰 수를 고르길 반복한다. 포인트는 매번 젤 큰수를 선택할때 범위를 지정하는 것인데, 예를 들어 남은 숫자가 5자리이고 5개의 숫자를 골라야 한다면 매번 고를 숫자는 한 가지로 고정 될 것이다. 이번 문제는 말 그래도 그리디(탐욕법) 알고리즘이 무엇인지 느낌이 확 와닿는 문제였다.

 

코드

import  java.util.*;
class Solution {
    public String solution(String number, int k) {
        String answer;
        
        int length = number.length();
        int count = length - k;
        int left = 0;
        int right = length - count;
        int idx=0, max, temp;
        
        StringBuilder sb = new StringBuilder();
        while(count>0) {
            max = -1;
            for(int i = left; i<=right; i++) {
                temp = number.charAt(i) - '0';
                if(temp>max) {
                    max = temp;
                    idx = i;
                }
            }
            sb.append(number.charAt(idx));
            count--;
            left = idx+1;
            right = length - count;
        }
        
        
        return answer = sb.toString();
    }
}

문제 설명

 

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.

전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

 

제한사항

  • 전체 학생의 수는 2명 이상 30명 이하입니다.
  • 체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
  • 여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.

나의 풀이

 이번 문제풀이는 여러 방법으로 풀어봤는데, 실행은 대부분 가능했지만 Programmers 테스트 통과는 실행시간 제한이 있었기 때문에 아래 코드만 가능했다. 접근은 잃어버린 사람 중 여벌이 있는 사람은 제외하고, 여벌이 없는 사람은 잃어버리지 않았으면서 여벌이 있는 사람과의 거리가 1 차이 나면 빌려오도록 구현 했다. 코드1로 먼저 구현하였는데 다른 사람 풀이 중 가장 눈에 띄는 접근법을 이용해서 코드2도 작성해 보았다. 접근방법만 세웠다면 코드 자체는 쉬운수준.

 

 

코드1

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        int answer = n - lost.length;
        
        for(int i=0; i<lost.length; i++) {
            for(int j=0; j<reserve.length; j++) {
                if(lost[i]==reserve[j]) {
                    answer++;
                    lost[i] = -1;
                    reserve[j] = -1;
                    break;
                }
            }
        }
        
        for(int i=0; i<lost.length; i++) {
            if(lost[i]<0) continue;
            for(int j=0; j<reserve.length; j++) {
                if(reserve[j]<0) continue;
                if(Math.abs(lost[i]-reserve[j])==1) {
                    answer++;
                    lost[i] = -1;
                    reserve[j] = -1;
                    break;
                }
            }
        }
        return answer;
    }
}

 

코드2

class Solution {
    public int solution(int n, int[] lost, int[] reserve) {
        int answer = n;
        int[] students = new int[n+2];
        for(int l : lost) {
            students[l]--;
        }
        for(int r : reserve) {
            students[r]++;
        }
        
        for(int i=1; i<students.length-1; i++) {
            if(students[i] == -1) {
                if(students[i-1] == 1) {
                    students[i]++;
                    students[i-1]--;
                } else if(students[i+1] == 1) {
                    students[i]++;
                    students[i+1]--;
                } else {
                    answer--;
                }
            } 
        }
        
        return answer;
    }
}

20.08.15 기준으로 개편된 문제 기준입니다.

 

문제 설명

Leo는 카펫을 사러 갔다가 아래 그림과 같이 중앙에는 노란색으로 칠해져 있고 테두리 1줄은 갈색으로 칠해져 있는 격자 모양 카펫을 봤습니다.

Leo는 집으로 돌아와서 아까 본 카펫의 노란색과 갈색으로 색칠된 격자의 개수는 기억했지만, 전체 카펫의 크기는 기억하지 못했습니다.

Leo가 본 카펫에서 갈색 격자의 수 brown, 노란색 격자의 수 yellow가 매개변수로 주어질 때 카펫의 가로, 세로 크기를 순서대로 배열에 담아 return 하도록 solution 함수를 작성해주세요.

 

제한사항

  • 갈색 격자의 수 brown은 8 이상 5,000 이하인 자연수입니다.
  • 노란색 격자의 수 yellow는 1 이상 2,000,000 이하인 자연수입니다.
  • 카펫의 가로 길이는 세로 길이와 같거나, 세로 길이보다 깁니다.
  •  

문제 풀이

 카펫의 가로를 x, 세로를 y라 하였을 때, brown = 2x + 2y -2 이고, yellow = (x-2)*(y-2) 의 수식을 기준으로 코드를 작성하였다. yellow의 최소값은 x가최대, y가 최소일때 이고, yellow의 최대값은 x==y 일 경우이기 때문에 최소값부터 값을 증가시키며 yellow와 일치하는지 검색하였다.

 

코드

class Solution {
    public int[] solution(int brown, int yellow) {
        int[] answer = {0,0};
        int x = brown/2 -1;
        int y = 3;
        
        while(x>=y) {
            if((x-2)*(y-2) == yellow) {
                answer[0] = x;
                answer[1] = y;
                break;
            } else {
                x--;
                y++;
                continue;
            }
        }
        return answer;
    }
}

문제

2차원 평면 위의 점 N개가 주어진다. 좌표를 x좌표가 증가하는 순으로, x좌표가 같으면 y좌표가 증가하는 순서로 정렬한 다음 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 점의 개수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에는 i번점의 위치 xi와 yi가 주어진다. (-100,000 ≤ xi, yi ≤ 100,000) 좌표는 항상 정수이고, 위치가 같은 두 점은 없다.

출력

첫째 줄부터 N개의 줄에 점을 정렬한 결과를 출력한다.

 

나의 풀이

 문제의 접근방법으로는 Collection의 sort를 사용하기 위해 class에 비교함수를 오버라이드 할 수 있는지라고 생각하고 접근. 받은 값에 따라 Point class의 객체를 생성하고 list에 저장했다. Point class에는 x값이 클수록, y값이 클수록 우선순위가 높도록 compareTo 메서드를 오버라이드 하고 list를 정렬 후 출력했다. java다운 코드 느낌이라 재밌게 금방 풀었다.

코드

import java.util.*;
import java.io.*;

class Point implements Comparable<Point>{
    
    int x;
    int y;
    
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    @Override
    public int compareTo(Point p2) {
        if(this.x>p2.x) {
            return 1;
        } else if(this.x<p2.x){
            return -1;
        } else {
            if(this.y>p2.y) {
                return 1;
            } else if(this.y<p2.y) {
                return -1;
            } else {
                return 0;
            }
        }
    }
    
    @Override
    public String toString() {
        return x+" "+y;
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int num = Integer.parseInt(br.readLine());
        StringTokenizer st;
        List<Point> list = new ArrayList<>();
        for(int i =0; i<num; i++) {
            st = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());
            list.add(new Point(x,y));
        }
        
        Collections.sort(list);
        for(int i=0; i<list.size(); i++) {
            bw.write(list.get(i).toString() + "\n");
        }
        
        bw.flush();
    }
}

문제

배열을 정렬하는 것은 쉽다. 수가 주어지면, 그 수의 각 자리수를 내림차순으로 정렬해보자.

입력

첫째 줄에 정렬하고자하는 수 N이 주어진다. N은 1,000,000,000보다 작거나 같은 자연수이다.

출력

첫째 줄에 자리수를 내림차순으로 정렬한 수를 출력한다.

 

나의 풀이

 주어진 숫자를 배열이든 Collection이든 넣고 내림차순 정렬을 하면 끝. 단 Collection은 중복을 허용하는 List를 사용했다. 

코드

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int num =Integer.parseInt(br.readLine());
        ArrayList<Integer> list = new ArrayList<>();
        
        while(num>0) {
            list.add(num%10);
            num /=10;
        }
        
        Collections.sort(list, Collections.reverseOrder());
        StringBuilder sb= new StringBuilder();
        for(int i=0; i<list.size(); i++) {
            sb.append(list.get(i));
        }
        bw.write(sb.toString()+"\n");
        bw.flush();
    }
}

문제

수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자.

  1. 산술평균 : N개의 수들의 합을 N으로 나눈 값
  2. 중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
  3. 최빈값 : N개의 수들 중 가장 많이 나타나는 값
  4. 범위 : N개의 수들 중 최댓값과 최솟값의 차이

N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.

출력

첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값을 출력한다.

둘째 줄에는 중앙값을 출력한다.

셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력한다.

넷째 줄에는 범위를 출력한다.

 

나의 풀이

 문제는 이전에 풀었던 카운터정렬 문제와 유사하고 단지 정렬된 배열에 어찌 접근할 것인지 정도만 추가된 문제였다. 주어진 값들을 읽어서 배열에 저장하고, 카운터 정렬을 실행 하였다. 주어지는 값이 -4000 ~ 4000 이기 때문에 배열의 인덱스와 맞추기 위해 모든 값에 4000을 더하며 카운터 정렬을 실행했다. 카운터 정렬 후 가장 빈도가 높은 값을 List에 저장하고, List의 길이를 체크해서 최빈값 중 두번째로 작은 값을 찾았다. 처음 접근법을 생각한 후 어렵지 않게 코드가 작성되어서 다른 사람들의 풀이와 비교해보았지만 대동소이 했고 실행 시간은 상당히 빠른편이었다. 단 모듈화가 되어 있지 않은점은 아쉬운점.

 

코드

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int num = Integer.parseInt(br.readLine());
        int[] arr = new int[num];
        
        int sum=0;
        for(int i=0; i<num; i++) {
            arr[i] = Integer.parseInt(br.readLine());
            sum += arr[i];
        }
        
        int[] countArr = new int[8001];
        for(int n : arr) {
            countArr[n+4000]++;
        }
        
        int[] ansArr = new int[num];
        int tempMax = 0, tempNum=0 , index=0;
        for(int i=0; i<countArr.length; i++) {
            tempNum = countArr[i];
            if(tempNum>0) {
                if(tempNum > tempMax) {
                    tempMax = tempNum;
                }
                
                while(tempNum>0) {
                    ansArr[index] = i-4000;
                    tempNum--;
                    index++;
                }
            }
        }
        
        ArrayList<Integer> list = new ArrayList<>();
        int most;
        for(int i=0; i<countArr.length; i++) {
            if(countArr[i]==tempMax) {
                list.add(i-4000);
            }
        }
        
        if(list.size()>1) {
            most = list.get(1);
        } else {
            most = list.get(0);
        }
        
        bw.write((int)Math.round((double)sum/num)+"\n");
        bw.write(ansArr[ansArr.length/2]+"\n");
        bw.write(most+"\n");
        bw.write(ansArr[ansArr.length-1]-ansArr[0]+"\n");
        
        
        
        bw.flush();
    }
}

문제

N개의 수가 주어졌을 때, 이를 오름차순으로 정렬하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 10,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 숫자가 주어진다. 이 수는 10,000보다 작거나 같은 자연수이다.

출력

첫째 줄부터 N개의 줄에 오름차순으로 정렬한 결과를 한 줄에 하나씩 출력한다.

 

나의 풀이

 카운터 정렬을 사용하라는 문제 설명이 있었기 때문에 진정한? 카운터 정렬로 먼저 코드를 작성했다. (코드2) 카운터 정렬의 특성한 (1, 0, 10000, 1, 2) 와 같이 입력한 숫자중에 공백이 넓은 경우 효율성이 떨어지기 때문에 코드2와 같이 작성하는 것이 일반적이다 라고 학습했었다. 결과적으론 테스트는 통과 되었는데 주어진 문제에서는 그냥 더 단순한 코드로 실행이 가능할 것 같아서 코드1도 작성해보았다. 

 

코드1

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int num = Integer.parseInt(br.readLine());
        int[] countArr = new int[10001];
        
        for(int i=0; i<num; i++) {
            countArr[Integer.parseInt(br.readLine())]++;
        }
        
        for(int i=1; i<countArr.length; i++) {
            int temp = countArr[i];
            while(temp>0) {
                bw.write(i+"\n");
                temp--;
            }
        }
        
        
        bw.flush();
    }
}

코드2

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int num = Integer.parseInt(br.readLine());
        int[] arr = new int[num+1];
        int[] ansArr = new int[num+1];
        int[] countArr = new int[10001];
        
        
        for(int i=1; i<=num; i++) {
            arr[i] = Integer.parseInt(br.readLine());
        }
        
        for(int n : arr) {
            countArr[n]++;
        }
        for(int i=1; i<10000; i++) {
            countArr[i+1] += countArr[i];
        }
        
        
        for(int i=num; i>=1; i--) {
            ansArr[countArr[arr[i]]] = arr[i];
            countArr[arr[i]]--;
        }
        
        for(int i=1; i<=num; i++) {
            bw.write(ansArr[i]+"\n");
        }
        
        
        bw.flush();
    }
}

풀이와 문제는 아래에

 

코드

import java.util.*;

class Solution {
    public int solution(String numbers) {
        int answer = 0;
        String[] tempArr = numbers.split("");
        int[] arr = new int[tempArr.length];
        for(int i=0; i<arr.length; i++) {
            arr[i] = Integer.parseInt(tempArr[i]);
        }
        
        Set<Integer> set = new HashSet<>();
        for(int i=1; i<=arr.length; i++) {
            perm(tempArr, 0, i, set);
        }
        answer = set.size();
        return  answer;
    }
    
    public void perm(String[] arr, int depth, int k, Set set) {
        //depth는 재귀함수 깊이, k는 만들어 낼 숫자의 길이
        if(depth==k) {  //숫자의 길이만큼 재귀 반복했는지
            StringBuilder sb = new StringBuilder();
            for(int i=0; i<k; i++) {
                sb.append(arr[i]);
            }
            isPrime(set, sb.toString());
            return;
        } else {
            for(int i=depth; i<arr.length; i++) {
                swap(arr, i, depth);
                perm(arr,depth+1,k,set);
                swap(arr,i,depth);
            }
        }
    }
    
    public void swap(String[] arr, int idx1 , int idx2) {
        String temp = arr[idx1];
        arr[idx1] = arr[idx2];
        arr[idx2] = temp;
    }
    
    public void isPrime(Set set, String str) {
        int num = Integer.parseInt(str);
        
        if(num<=1 || (num!=2 && num%2==0)) return;
        boolean flag = true;
        for(int i=3; i<=(int)Math.sqrt(num); i+=2) {
           if(num%i==0) {
                flag = false;
                break;
            }
        }
        if(flag) {
            set.add(num);
        }
    }
    
}

 

문제 풀이

 

 문제를 처음 접할 땐 소수찾기와 재귀함수로 구성되겠구나 금방 생각 할 수 있었는데, 소수찾기 알고리즘은 이미 여러 방법으로 몇번이나 구현해 보았기 때문에 수월했다. 문제는 주어지는 숫자를 조합해서 모든 경우의 수를 찾아내는 것이었다. 이를 순열 알고리즘 이라고 부르고 이를 구현하기 위해선 재귀함수의 이해가 필수적이었다. 결과적으론 순열 함수 알고리즘은 따로 학습해서 이번 문제풀이에 적용해야 했기에 시간이 오래걸렸다... 중복값 제거는 단순하게 set에 넣어만 주면 되기 때문에 쉽게 해결할 수 잇었다.

 

 

문제 설명

 

한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다.

각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이 조각으로 만들 수 있는 소수가 몇 개인지 return 하도록 solution 함수를 완성해주세요.

 

제한사항

  • numbers는 길이 1 이상 7 이하인 문자열입니다.
  • numbers는 0~9까지 숫자만으로 이루어져 있습니다.
  • 013은 0, 1, 3 숫자가 적힌 종이 조각이 흩어져있다는 의미입니다.

 

+ Recent posts