<문제>

 

타노스는 프로그램의 균형을 위해서는 리스트의 원소 절반을 무작위로 삭제해야 한다고 믿고 있다.
타노스가 손가락을 튕겼을 때(프로그램을 실행했을 때) 입력된 리스트에서 절반의 원소를 무작위로 삭제하여 리턴하는 인피니티 건틀렛 프로그램을 작성하시오. (무작위 삭제이므로 입력값이 같아도 출력값이 매번 달라야 합니다)

 

<조건>

Scanner 이용

<입력 예시>
[2, 3, 1, 6, 5, 7]

<출력 예시 1>
[2, 5, 7]

<출력 예시 2>
[3, 6, 5]

<참고>

리스트의 원소가 홀수개일 경우 절반의 확률로 절반보다 많은 원소가 삭제되거나 절반보다 적은 원소가 삭제되어야 합니다.

(만약 리스트의 원소가 7개라면 절반의 확률로 3개 또는 4개의 원소가 삭제됨)

 


 

<달거북씨 코드>

package april3W;

import java.util.ArrayList;
import java.util.Scanner;

public class Thanos_Jack{
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		ArrayList<Integer> arList = new ArrayList<>();
		
		while (true) {
			System.out.println("원소 리스트에 들어갈 숫자를 입력하세요. 나가기 : q");
			String num;
			// 숫자와 q 외에 다른 문자가 들어갔을 때 예외처리
			try {
				// ArrayList에 들어갈 숫자 입력받기
				num = sc.next();
				if(num.equalsIgnoreCase("q")) {
					System.out.println("숫자 입력을 종료합니다.");
					break;
				} 

				// 입력받은 숫자 ArrayList에 담기
				arList.add(Integer.parseInt(num));
				
			} catch (NumberFormatException e) {
				System.out.println("잘못 입력했습니다.");
				continue;
			}
		}
		
		// 입력받은 원소리스트 출력
		System.out.print("\n>>> 원소 리스트 [ ");
		for (int i = 0; i < arList.size(); i++) {
			System.out.print(arList.get(i) + " ");
		}
		System.out.println(" ]");	// arList size : 3
		
		// arList에서 어느 자리의 원소를 지울지 결정하는 인덱스 배열
		// arList의 절반을 지워야하므로, arList size의 반을 길이로 할당한다.
		int[] index = new int[arList.size()/2];
		// arList의 원소가 홀수일 경우 추가적인 작업을 위한 변수
		
		int r;
		
		// arList가 홀수인 경우
		if(arList.size()%2 != 0) {
			// 랜덤으로 0 또는 1을 받는다.(50%로의 확률로 가정)
			r = Util.getInstance().random(0, 1);
			
			// r이 0인 경우엔 조건식을 충족하지 않아 for문이 돌아가지 않는다.
			// 즉, for문은 돌아가지 않거나 딱 한 번 돌아간다.
			for (int i = 0; i < r; i++) {
				index[i] = Util.getInstance().random(0, arList.size()-1);
				arList.remove(index[i]);	// 0~3 자리 중 하나가 랜덤으로 지워진다.
			}
		}
		
		// 만약 1이 나왔다면 현재 arList는 처음 받은 size보다 1 작은 상태
		// 0이라면 처음 size 그대로
		
		// 인덱스의 길이만큼 for문을 돌려서 인덱스 배열은 채워주고,
		// 받아온 인덱스로 arList의 특정 위치의 값을 지워준다.
		for (int i = 0; i < index.length(); i++) {
			index[i] = Util.getInstance().random(0, arList.size()-1);
			arList.remove(index[i]);
		}
		System.out.println("===========================================================");
		System.out.print("핑거스냅 후, 원소 리스트 [ ");
		for (int i = 0; i < arList.size(); i++) {
			System.out.print(arList.get(i) + " ");
		}
		System.out.println(" ]");
	}
}

 

<해설>

1.

리스트에 원소가 몇 개까지 들어가는지, 주어진 크기가 없으므로 ArrayList 사용하여 입력받는다.

 

2.

숫자 외 다른 문자열이 들어갈 경우를 예외처리해주고 String 타입으로 받은 num을 Integer로 형변환시켜준다.

 

3.

인덱스 배열의 크기를 ArrayList 사이즈의 절반 크기로 지정한다.

이 배열의 길이로 아래에서 원소를 지울 때 몇 개를 지우는지 결정된다.

 

4. 

원소의 개수가 짝수인 경우, 인덱스의 길이만큼 for문을 돌면서 랜덤으로 인덱스 배열을 채워준다.

배열 안에 들어가는 숫자는 0보다 크고 ArrayList 사이즈보다 하나 작다.

즉, ArrayList의 인덱스 번호(0부터 arList.size()-1 사이의 값)를 값으로 갖고, 해당 인덱스의 ArrayList 원소를 지워준다.

 

5. 홀수인 경우, 50%의 확률로 0과 1을 랜덤 부여하여 1일 경우 원소를 추가로 지워준다.

 


 

기본적으로 다른 스터디원들도 코드의 틀은 비슷비슷 함. 다만 50%의 확률로 절반 이상이 삭제되도록 구현하는 코드들이 저마다 달랐다.

 

달거북씨의 경우,

// arList가 홀수인 경우
if(arList.size()%2 != 0) {
	// 랜덤으로 0 또는 1을 받는다.(50%로의 확률로 가정)
	r = Util.getInstance().random(0, 1);
	
	// r이 0인 경우엔 조건식을 충족하지 않아 for문이 돌아가지 않는다.
	// 즉, for문은 돌아가지 않거나 딱 한 번 돌아간다.
	for (int i = 0; i < r; i++) {
		index[i] = Util.getInstance().random(0, arList.size()-1);
		arList.remove(index[i]);	// 0~3 자리 중 하나가 랜덤으로 지워진다.
	}

랜덤으로 0과 1이 부여되는 확률이 50%라고 가정하고 1일 경우엔 for문 안으로 들어가 arList의 원소를 한 번 더 지워주는 코딩을 짰다.

 

스터디원 S씨의 경우,

if(rd.nextInt(2) == 0) {					
	// Math.ceil : 주어진 숫자보다 크거나 같은 숫자 중 가장 작은 숫자를 integer로 반환
	a = (int)Math.ceil(arr.length/2.0);		
}else {
	// Math.floor : 숫자를 가장 가까운 정수 또는 지정된 유의값의 배수로 내린다.
	// 음수일 경우 모드에 따라 0에 가까워지거나 0에서 멀어지는 방향으로 올림
	a = (int)Math.floor(arr.length/2.0);	
}

달거북씨와 동일하게 랜덤으로 0과 1을 부여한다.

이후 Math.ceil()과 Math.floor()를 사용해 arr.length의 절반만 지울지, 절반보다 많이 지울지를 결정했다.

Math.ceil()은 내림, Math.floor()는 올림이라고 생각하면 쉽다.

 

스터디원 Y씨의 경우,

(int)Math.round(Math.random() + thanos_list.length / 2);

Math.random()의 성격을 응용했다.

Math.random()은 0 이상 1 미만의 자연수이므로 0~0.99999...가 랜덤으로 부여된다. 

이때 Math.round()를 씌워주면, 0.49999...까지는 0으로 내려지고, 0.5부터는 반올림되어 원소리스트에 1이 더해지게 된다.

728x90

대체 왜 상금 받는 가정을 천 번이나 돌려보는 거지...

 


 

2017년에 이어, 2018년에도 카카오 코드 페스티벌이 개최된다!

카카오 코드 페스티벌에서 빠질 수 없는 것은 바로 상금이다. 
2017년에 개최된 제1회 코드 페스티벌에서는, 본선 진출자 100명 중 21명에게 아래와 같은 기준으로 상금을 부여하였다.

2018년에 개최될 제2회 코드 페스티벌에서는 상금의 규모가 확대되어, 본선 진출자 64명 중 31명에게 아래와 같은 기준으로 상금을 부여할 예정이다.

 

2017, 2018년도 상금 기준

 

제이지는 자신이 코드 페스티벌에 출전하여 받을 수 있을 상금이 얼마인지 궁금해졌다. 
그는 자신이 두 번의 코드 페스티벌 본선 대회에서 얻을 수 있을 총 상금이 얼마인지 알아보기 위해, 상상력을 발휘하여 아래와 같은 가정을 하였다.

 

제1회 코드 페스티벌 본선에 진출하여 a등(1 ≤ a ≤ 100)등을 하였다. 단, 진출하지 못했다면 a = 0으로 둔다.
제2회 코드 페스티벌 본선에 진출하여 b등(1 ≤ b ≤ 64)등을 할 것이다. 단, 진출하지 못했다면 b = 0으로 둔다.

 

제이지는 이러한 가정에 따라, 자신이 받을 수 있는 총 상금이 얼마인지를 알고 싶어한다.
  
첫 번째 줄에 제이지가 상상력을 발휘하여 가정한 횟수 T(1 ≤ T ≤ 1,000)가 주어진다.

<입력>
다음 T개 줄에는 한 줄에 하나씩 제이지가 해본 가정에 대한 정보가 주어진다. 
각 줄에는 두 개의 음이 아닌 정수 a(0 ≤ a ≤ 100)와 b(0 ≤ b ≤ 64)가 공백 하나를 사이로 두고 주어진다.
  
<출력>
각 가정이 성립할 때 제이지가 받을 상금을 원 단위의 정수로 한 줄에 하나씩 출력한다.
입력이 들어오는 순서대로 출력해야 한다.


 

<달거북씨 코드>

public static void main(String[] args) {
	// 천 번 가정
	// 귀찮으니까 랜덤으로 준다
	for(int T=0; T<1000; T++) {
		// a는 2017년, b는 2018년
		// 초기화
		int a = 0;
		int b = 0;
		
		// 본선 진출하지 못한 경우 = 0
		// 2017년 본선은 100등까지이므로, 본선 진출 못한 경우부터 100등인 경우까지 랜덤으로 값 부여
		// 2018년 본선은 64등까지이므로, 본선 진출 못한 경우부터 64등인 경우까지 랜덤으로 값 부여
		a = Util.getInstance().random(0, 100);
		b = Util.getInstance().random(0, 64);
		
		// 2017년, 2018년 상금 변수 초기화
		int aReward = 0;
		int bReward = 0;

		// 2017년의 경우
		if (a==0) {
			aReward = 0;
		} else {
			if (a == 1) {
				aReward = 500;
			} else if (a<4) {
				aReward = 300;
			} else if (a<7) {
				aReward = 200;
			} else if (a<11) {
				aReward = 50;
			} else if (a<16) {
				aReward = 30;
			} else if (a<22) {
				aReward = 10;
			} else {
				aReward = 0;
			}
		}
		
		// 2018년의 경우
		if (b==0) {
			bReward = 0;
		} else {
			if (b == 1) {
				bReward = 512;
			} else if (b<4) {
				bReward = 256;
			} else if (b<8) {
				bReward = 128;
			} else if (b<16) {
				bReward = 64;
			} else if (b<32) {
				bReward = 32;
			} else {
				bReward = 0;
			}
		}
		System.out.println("< " + (T+1) + "번 가정 >" + "	" + 
				"2017년 : " + (a!=0? a + "등" : "순위권 밖") + "	" + 
				"2018년 : " + (b!=0? b + "등" : "순위권 밖") + "	" + 
				"->" + "	" + "상금 :" + (aReward + bReward) + "만원");		
	}

 

<해설>

1.

for문으로 천 번 가정해보고, int a와 b에 각각 2017년, 2018년 순위를 랜덤으로 부여한다.

 

2.

2017년, 2018년 리워드가 들어갈 변수를 선언해주고 2017년의 경우와 2018년의 경우를 각각 if문으로 코딩한다.

tip. if문이 무척 길어지는데, 스터디원 분이 본선에 진출하지 못한 경우인 0도 else문으로 처리하면 된다고 하셨다. 적어도 한 줄씩은 줄일 수 있음

 

3.

삼항연산자를 통해 2017년, 2018년 순위가 각각 0이면 순위권 밖, 아니면 해당 등수가 출력되게 코딩하고, 두 연도의 리워드를 합해서 출력토록 하였다.

 

<출력결과 예시>

.

.

.

< 995번 가정 > 2017년 : 43등 2018년 : 61등 -> 상금 :0만원
< 996번 가정 > 2017년 : 47등 2018년 : 15등 -> 상금 :64만원
< 997번 가정 > 2017년 : 80등 2018년 : 30등 -> 상금 :32만원
< 998번 가정 > 2017년 : 43등 2018년 : 12등 -> 상금 :64만원
< 999번 가정 > 2017년 : 39등 2018년 : 34등 -> 상금 :0만원
< 1000번 가정 > 2017년 : 20등 2018년 : 14등 -> 상금 :74만원 

 

TMI. 주어진 문제대로 코드 형식을 짜야하는데 잘 못하겠다. 코딩테스트 연습하려면 주어진 형식 대로 해야할 텐데 계속 편리한 랜덤을 쓰게 된다.연습하자 달거북씨야ㅠ

 


 

< 스터디원 Y씨 풀이>

public class Price_jys {
	Scanner sc = new Scanner(System.in);
	
	private int times = 0;
	private int[][] ranking;
	// 상금을 미리 배열에 넣어준다.
	private int[] prize17 = {5000000, 3000000, 2000000, 500000, 300000, 100000};
	private int[] prize18 = {5120000, 2560000, 1280000, 640000, 320000};
	private int[] total_prize;
	
	// 가정 횟수 입력
	public Price_jys() {
		System.out.println("몇 번 상상쓰?");
		this.times = sc.nextInt();
	}
	
	// getter, setter 생성
	public int[] getTotal_prize() {
		return total_prize;
	}
	
	public void setTotal_prize(int[] total_prize) {
		this.total_prize = total_prize;
	}
	
	// 2017년, 2018년 랭킹이 들어가는 이중배열과
	// 두 연도의 상금 합계를 받는 배열을 초기화
	public void setArr() {
		if( this.times >= 1 && this.times <= 1000 ) {
			this.ranking = new int[this.times][2];
			this.total_prize = new int[this.times];
		}
	}
	
	// 2017년, 2018년 랭킹을 입력받는다.
	public void setRank() {
		for( int i = 0; i < this.times; i++ ) {
			System.out.println((i+1) + "번째 예상의 17년도 등수를 입력하시오!");
			this.ranking[i][0] = sc.nextInt();
			
			System.out.println((i+1) + "번째 예상의 18년도 등수를 입력하시오!");
			this.ranking[i][1] = sc.nextInt();
			
			// Arrays.toString(Value)은 배열 내용을 출력해준다. 
			System.out.println(Arrays.toString(this.ranking[i]));
		}
	}

	// 상금 배열 메소드 정의
	public int[] reward() {
		
		// 가정 횟수만큼 for문을 돌린다.
		for( int i = 0; i < this.times; i++ ) {		
			int sum1 = 0;
			int sum2 = 0;
			
			// 17년도의 상금을 total_prize의 해당 인덱스에 넣어준다.
			for( int j = 1; j <= this.prize17.length; j++ ) {
				// 1등은 1명, 2등은 2명, 3등은 3명, ... 
               	 		// 누적을 위해 sum1 사용
				sum1 += j;
				// 해당 등수에 속하면 if문 안으로 들어간다.
				if( this.ranking[i][0] <= sum1 ) {
					// 해당 인덱스의 17년도 상금이 total_prize에 더해진다. 
					this.total_prize[i] += prize17[j-1];
					break;
				}					
			}
			System.out.println(i + " " + this.total_prize[i]);
		
			// 18년도 상금을 total_prize의 해당 인덱스에 넣어준다.
			for( int k = 0; k < this.prize18.length; k++ ) {
				// 1등은 1명, 2등은 2명, 3등은 4명, ... 
                		// 2배수가 되므로 Math.pow사용으로 sum2에 누적시킴
				sum2 += (int)Math.pow(2, k);
				// 해당 등수에 속하면 if문 안으로 들어간다.
				if( this.ranking[i][1] <= sum2) {
					// 해당 인덱스의 18년도 상금이 total_prize에 더해진다.
					this.total_prize[i] += prize18[k];
					break;
				}			
			}
			System.out.println(i + " " + this.total_prize[i]);	
		
		}
		return this.total_prize;
		
	}
}

 

처음부터 상금을 배열에 넣고 시작

2017년도와 2018년도의 등수에 규칙성을 찾아내서 코드를 짜고, 해당 등수에 들면 배열의 상금을 total 배열에 넣어 더해준다.

아, 출력결과를 실행하는 메인클래스는 따로 있다.

 

TMI.

if문 줄줄이 나열하는 것보다 깔끔한데 하라고 하면, Y씨처럼 짜라면 할 수 있을지는 잘 모르겠다.

아직까진 머리가 주먹구구식으로 돌아간다.

728x90

오랜만의 코드리뷰 시간

스터디는 계속 쭉 해왔는데 중간에 프로젝트 하랴 셤 보랴 하면서 문제풀이가 별로 없었다.

안 올린 것도 있구...

DB로 넘어가면서 SQL 문제도 매일 풀었는데 그거라도 올릴 걸 그랬나...

TMI 끝!

 


<문제>

XX게임에는 피로도 시스템(0 이상의 정수로 표현합니다)이 있으며, 일정 피로도를 사용해서 던전을 탐험할 수 있습니다.
이때, 각 던전마다 탐험을 시작하기 위해 필요한 "최소 필요 피로도"와 던전 탐험을 마쳤을 때 소모되는 "소모 피로도"가 있습니다. 


- "최소 필요 피로도"는 해당 던전을 탐험하기 위해 가지고 있어야 하는 최소한의 피로도

- "소모 피로도"는 던전을 탐험한 후 소모되는 피로도

 

예를 들어 "최소 필요 피로도"가 80, "소모 피로도"가 20인 던전을 탐험하기 위해서는 유저의 현재 남은 피로도는 80 이상 이어야 하며, 던전을 탐험한 후에는 피로도 20이 소모됩니다.

 

이 게임에는 하루에 한 번씩 탐험할 수 있는 던전이 여러개 있는데, 한 유저가 오늘 이 던전들을 최대한 많이 탐험하려 합니다. 유저의 현재 피로도 k와 각 던전별 "최소 필요 피로도", "소모 피로도"가 담긴 2차원 배열 dungeons 가 매개변수로 주어질 때, 유저가 탐험할수 있는 최대 던전 수를 return 하도록 solution 함수를 완성해주세요.

 

<제한사항>
- k는 1 이상 100 이하인 자연수입니다.
- dungeons의 세로(행) 길이(즉, 던전의 개수)는 1 이상 8 이하입니다.
- dungeons의 가로(열) 길이는 2 입니다.
- dungeons의 각 행은 각 던전의 ["최소 필요 피로도", "소모 피로도"] 입니다.
- "최소 필요 피로도"는 항상 "소모 피로도"보다 크거나 같습니다.
- "최소 필요 피로도"와 "소모 피로도"는 1 이상 100 이하인 자연수입니다.
- 서로 다른 던전의 ["최소 필요 피로도", "소모 피로도"]가 서로 같을 수 있습니다.


 

<달거북씨 코드>

public class game {

	public static void main(String[] args) {
		// 객체 생성
		Dungeons d = new Dungeons();
		// dungeons() 메서드로 던전 크기와 피로도 결정 및 출력
		d.dungeons();
		// solution() 메서드로 유저가 탐험할 수 있는 최대 던전 수를 카운트 한 후 출력
		System.out.println("유저의 최대 탐험 가능 던전 개수 : " + d.solution());
	}
}

// 던전 클래스
class Dungeons{
	// 최소 필요 피로도와 소모 피로도가 들어갈 이중배열
	int[][] arr;
	
	public void dungeons() {
		// 최소 필요 피로도
		int min = 0;
		// 소모 피로도
		int use = 0;
		
		// 전역변수 배열 arr의 크기를 정해준다. > 랜덤
		// 행의 길이는 1~8(던전 개수), 열의 길이는 2(최소 필요 피로도, 소모 피로도)
		this.arr = new int[Util.getInstance().random(1, 8)][2];
		for (int i = 0; i < arr.length; i++) {
			// 1에서 100까지 최소 필요 피로도의 범위를 지정해준다.
			min = Util.getInstance().random(1, 100);
			// 소모 피로도는 최소 필요 피로도보다 작거나 같다.
			// 1부터 최소 필요 피로도까지 범위를 지정한다.
			use = Util.getInstance().random(1, min);
			// 0열에는 최소 필요 피로도
			arr[i][0] = min;
			// 1열에는 소모 피로도가 들어간다.
			arr[i][1] = use;
		}
		
		// 던전의 크기가 궁금하므로 출력해본다.
		for (int i = 0; i < arr.length; i++) {
			System.out.print("던전 " + (i+1) + " : ");
			for (int j = 0; j < arr[i].length; j++) {
				System.out.print(arr[i][j]+ " ");
			}
			System.out.println();
		}
	}
	
	// 문제에서 요구하는 유저가 탐험할수 있는 최대 던전 수를 return 하는 solution 함수
	public int solution() {
		// 유저의 피로도 k는 1~100까지의 수로 랜덤 지정
		// 궁금하니까 출력해준다.
		int k = Util.getInstance().random(1, 100);
		System.out.println("=============================");
		System.out.println("유저의 피로도 : " + k);
		
		// 던전을 몇 번 탐험 할 수 있는지 세어주는 변수
		int count = 0;
		for (int i = 0; i < arr.length; i++) {
				// 유저의 피로도가 최소 필요 피로도보다 클 경우 count가 올라간다.
				// 유저의 피로도는 소모 피로도만큼 깎인다.
				if(k>=arr[i][0]) {
					count++;
					k = k - arr[i][1];
					System.out.println("=============================");
					System.out.println("들어간 던전 : 던전" + (i+1) );
					System.out.println("현재 유저 피로도 : " + k);
				} else {
					continue;
					//break;
				}
		}
		System.out.println("=============================");
		System.out.println("더 이상 들어갈 수 있는 던전이 없습니다.");
		return count;	// for문 안에서 헤아린 count를 반환한다.
	}
}

 

<해설>

문제에서 요구하는 풀이방법은 DFS를 통한 풀이라고 한다.

하지만 DFS에 대해 잘 몰랐기 때문에 if문을 통해 모든 던전에 들려보는 방식을 취했다.

 

1.

던전 개수를 행으로 갖고 최소 필요 피로도(min)와 소모 피로도(use)를 각각 열로 갖는 이중배열을 만들어준다.

던전 개수는 랜덤으로 부여된다.

 

2.

최소 필요 피로도는 1이상 100 이하의 정수로 랜덤 부여된다. 싱글톤 객체를 이용해 랜덤을 돌려주었다.

 

3.

소모 피로도는 1이상 최소 필요 피로도 이하이므로 (1, min)으로 코딩하고 랜덤 부여한다.

 

4.

유저의 피로도(k)도 랜덤으로 부여한다.

 

5.

던전의 길이동안 탐험하는 for문 안에 if문 작성

처음에는 else문에서 더 이상 던전에 들어가지 못할 경우 바로 break;를 하고 for문을 나오도록 했는데,

모든 던전을 다 방문해야 한다는 설명을 듣고 continue;로 바꿔주었다.

continue;를 사용하면 들어갈 수 없는 던전은 패스하고 다음 던전으로 넘어간다.

 

<출력결과 예시>

던전 1 : 80 56 
던전 2 : 54 19 
던전 3 : 25 17 
던전 4 : 12 9 
던전 5 : 27 18 
던전 6 : 73 3 
=============================
유저의 피로도 : 82
=============================
들어간 던전 : 던전1
현재 유저 피로도 : 26
=============================
들어간 던전 : 던전3
현재 유저 피로도 : 9
=============================
더 이상 들어갈 수 있는 던전이 없습니다.
유저의 최대 탐험 가능 던전 개수 : 2

 


 

DFS 방식으로 푼 풀이도 있으면 좋겠지만 다른 스터디원들 모두 설명이 어렵다고 하여 패스.

나중에 공부해서 풀어보아야 겠다.

728x90

Q2.

 

 

 

A2. 달거북씨 코드

728x90

Q1.

 

 

 

A1. 달거북씨 코드

728x90

+ Recent posts