[문제]

동호와 규완이는 212호에서 문자열에 대해 공부하고 있다. 김진영 조교는 동호와 규완이에게 특별 과제를 주었다. 특별 과제는 특별한 문자열로 이루어 진 사전을 만드는 것이다. 사전에 수록되어 있는 모든 문자열은 N개의 "a"와 M개의 "z"로 이루어져 있다. 그리고 다른 문자는 없다. 사전에는 알파벳 순서대로 수록되어 있다.

규완이는 사전을 완성했지만, 동호는 사전을 완성하지 못했다. 동호는 자신의 과제를 끝내기 위해서 규완이의 사전을 몰래 참조하기로 했다. 동호는 규완이가 자리를 비운 사이에 몰래 사전을 보려고 하기 때문에, 문자열 하나만 찾을 여유밖에 없다.

N과 M이 주어졌을 때, 규완이의 사전에서 K번째 문자열이 무엇인지 구하는 프로그램을 작성하시오.

[입력]

첫째 줄에 N, M, K가 순서대로 주어진다. N과 M은 100보다 작거나 같은 자연수이고, K는 1,000,000,000보다 작거나 같은 자연수이다.

[출력]

첫째 줄에 규완이의 사전에서 K번째 문자열을 출력한다. 만약 규완이의 사전에 수록되어 있는 문자열의 개수가 K보다 작으면 -1을 출력한다.

[예제]

 


>문제 풀이

N개의 "a"와 M개의 "z"으로 만들수 있는 문자열을 사전순으로 정렬했을 때 K번째 문자열을 구하라.

N과 M이 100 이하의 자연수이므로 N과 M으로 조합 가능한 문자는 (200!)/(100!*100!) 개 이다.

즉, 이걸 하나씩 다 조합해보고 k번째 문자열을 구하기에는 시간도 메모리도 너무 비효율적이다.

그럼 어떻게 풀어야 할까?

사전순으로 a가 먼저이고 z가 다음이다. 즉, 조합할 때 순서가 있다는 것.

만약 a 3개와 z 3개로 만들 수 있는 문자열에 대해서 살펴본다고 했을 때

맨 앞글자가 a로 시작 + "a 2개와 z 3개로 만들 수 있는 문자열"

맨 앞글자가 z로 시작 + "a 3개와 z 2개로 만들 수 있는 문자열"

이렇게 될 것이다.

즉, dp[N][M]를 "a" N개와 "z" M개로 만들 수 있는 문자열의 개수 라고 할 때

dp[N][M]= dp[N-1][M] + dp[N][M-1] 의 점화식을 만들 수 있다.

** 여기까지는 금방 생각해 냈는데, 구현에서 막혔다.**

그래서 다른 분의 코드를 참고하였다.

참고한 블로그: https://velog.io/@sungjin0757/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B0%B1%EC%A4%80-1256%EB%B2%88-%EC%82%AC%EC%A0%84JAVA

 

[알고리즘] - 백준 1256번 : 사전(JAVA)

문제 풀러가기dp 문제를 풀이하는 방식으로 접근을 하여야 하지만, 좀 더 알아야 할 것이 있었습니다!n개의 문자와 m개의 문자를 조합하여 만들 수 있는 문자열의 개수를 구할 수 있는 것이 시급

velog.io


먼저 함수를 두 가지로 나눈다.

check 함수와 makeS함수

함수 1) check(int a, int z) //a는 "a"의 개수, z: "z"의 개수

"a" a개와 "z" z개로 만들 수 있는 문자열 개수를 구하는 함수이다.

함수 2) makeS(int a, int z, int k) //k는 구하고자하는 문자열의 순서

"a" a개와 "z" z개로 만들수 있는 문자열들 중 k번째 문자열을 구하는 함수

<main>

//"a" N개와 "z" M개로 만들 수 있는 문자열의 개수가 K보다 작으면 -1출력

if( check(N, M) < K ) 일 때 -1출력

else{

   makeS(N, M, K);

   문자열 출력;

}


check 함수는 점화식을 그대로 구현한 것이라 아래 코드를 보면 어렵지 않은 것을 알 수 있다.

makeS 함수도 어렵지는 않다.

(* 문자는 하나씩("a"||"z") 붙여줘야 하기 때문에 StringBuilder를 사용했다.)

분기점 1)

a==0일 때는 남은 a가 없으니까 뒤로는 z로만 채우면 되고

z==0일 때는 남은 z가 없으니까 뒤로는 a로만 채우면 된다.

분기점 2)

// a로 시작하게 됐을 때 만들 수 있는 문자열의 개수를 구해본다.

double check= check(a-1, z);

if ( check < k ) {

     // 이면, a로 시작하게 되면 k번째 문자열을 포함하는 문자열을 못만든다는 것이다.

     // 그러면 "z"를 붙여줘야하고

     // makeS(a, z-1, k-check);

     // 왜?? a는 안썼고, z는 하나 썼으며

     // k에서 check를 빼주는 이유: z로 시작함으로써 a로 시작하는 문자열들의 개수는 빼고 넘겨주는 것

}else{

     // check>=k 이면, a로 시작했을 때 k번째 문자열을 구할 수 있다.

     // "a"를 붙여주고

     // makeS(a-1, z, k);

     // 왜?? a 하나 썼고, z는 안썼다.

     //그리고 a로 시작 했을 때 k번째 문자열을 구할 수 있으니까 그대로 넘겨준다.

}

 

>전체 코드

 

import java.util.Scanner;

public class Main {
	static double[][] dp= new double[101][101];
	static StringBuilder sb= new StringBuilder(); 
	
	public static void main(String[] args) {
		Scanner scan= new Scanner(System.in);
		
		//N은 a개수, M은 z개수, K번째 문자열을 구해야함.
		//N, M은 100이하, K는 10억 이하의 수
		int N= scan.nextInt();
		int M= scan.nextInt();
		double K= scan.nextDouble();
		
		if(check(N, M)<K) {
			System.out.println("-1");
		}else {
			makeS(N, M, K);
			System.out.println(sb.toString());
		}
	}//main
	
	//개수 구하는 함수
	public static double check(int a, int z) {
		if(a==0||z==0) return 1;
		if(dp[a][z]!=0) return dp[a][z];
		
		return dp[a][z]= Double.min(check(a-1, z)+check(a, z-1), 1000000001);
	}
	
	//문자열 만드는 함수
	public static void makeS(int a, int z, double k) {
		if(a==0) {
			for(int i=0; i<z; i++)
				sb.append("z");
			return;
		}
		if(z==0) {
			for(int i=0; i<a; i++)
				sb.append("a");
			return;
		}
		
		double check= check(a-1, z);
		if(k>check) {
			sb.append("z");
			makeS(a, z-1, k-check);
		}else {
			sb.append("a");
			makeS(a-1, z, k);
		}
	}//makeS	
}

https://www.acmicpc.net/problem/1256

 

1256번: 사전

첫째 줄에 N, M, K가 순서대로 주어진다. N과 M은 100보다 작거나 같은 자연수이고, K는 1,000,000,000보다 작거나 같은 자연수이다.

www.acmicpc.net

 

[문제]

한 배열 A[1], A[2], …, A[n]에 대해서, 부 배열은 A[i], A[i+1], …, A[j-1], A[j] (단, 1 ≤ i ≤ j ≤ n)을 말한다. 이러한 부 배열의 합은 A[i]+…+A[j]를 의미한다. 각 원소가 정수인 두 배열 A[1], …, A[n]과 B[1], …, B[m]이 주어졌을 때, A의 부 배열의 합에 B의 부 배열의 합을 더해서 T가 되는 모든 부 배열 쌍의 개수를 구하는 프로그램을 작성하시오.

예를 들어 A = {1, 3, 1, 2}, B = {1, 3, 2}, T=5인 경우, 부 배열 쌍의 개수는 다음의 7가지 경우가 있다.

T(=5) = A[1] + B[1] + B[2]
= A[1] + A[2] + B[1]
= A[2] + B[3]
= A[2] + A[3] + B[1]
= A[3] + B[1] + B[2]
= A[3] + A[4] + B[3]
= A[4] + B[2]

[입력]

첫째 줄에 T(-1,000,000,000 ≤ T ≤ 1,000,000,000)가 주어진다. 다음 줄에는 n(1 ≤ n ≤ 1,000)이 주어지고, 그 다음 줄에 n개의 정수로 A[1], …, A[n]이 주어진다. 다음 줄에는 m(1 ≤ m ≤ 1,000)이 주어지고, 그 다음 줄에 m개의 정수로 B[1], …, B[m]이 주어진다. 각각의 배열 원소는 절댓값이 1,000,000을 넘지 않는 정수이다.

[출력]

첫째 줄에 답을 출력한다. 가능한 경우가 한 가지도 없을 경우에는 0을 출력한다.

[예제]

 


>문제 풀이

 

A[ ]: 1 3 1 2

listA: 1, 4, 5, 7, 3, 4, 6, 1, 3, 2

sort: 1, 1, 2, 3, 3, 4, 4, 5, 6, 7

B[ ]: 1 3 2

listB: 1, 4, 6, 3, 5, 2

sort: 1, 2, 3, 4, 5, 6

List에 각 배열로 만들 수 있는 부분합을 저장해놓고, lower bound와 upper bound를 구하여

listA.get(i) + listB.get(?)==T인 개수를 구한다.

단, lower bound와 upper bound를 구하기 전에 sort를 해줘야 한다.

lower bound는 listA.get(i)+listB.get(lb)가 T이상인 값을 구하고

upper bound는 listA.get(i)+listB.get(ub)가 T초과인 값을 구한다.

그리고 cnt+= ub-lb;


+) 투 포인터 탐색 방법을 사용할 수도 있다.

+) map을 활용할 수도 있는데 그러면 코드가 더 간결해진다.

 

 

 

>전체 코드

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.List;

public class Main {
	
	public static void main(String[] args) throws IOException {
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		
		int T, N, M; //-십억<=T<=십억 // N: 1000이하의 정수, M: 1000 이하의 정수
		long[] A, B; //각 원소는 절댓값 백만 이하
		List<Long> listA= new ArrayList<>();
		List<Long> listB= new ArrayList<>();
		
		//입력
		T= Integer.parseInt(br.readLine());
		N= Integer.parseInt(br.readLine());
		A= new long[N];
		st= new StringTokenizer(br.readLine());
		for(int i=0; i<N; i++) {
			A[i]= Long.parseLong(st.nextToken());
		}
		
		M= Integer.parseInt(br.readLine());
		B= new long[M];
		st= new StringTokenizer(br.readLine());
		for(int i=0; i<M; i++) {
			B[i]= Long.parseLong(st.nextToken());
		}
		
		//나올 수 있는 합 구해서 list에 넣기
		long sum=0;
		for(int i=0; i<N; i++) {
			sum= A[i];
			listA.add(sum);
			for(int j=i+1; j<N; j++) {
				sum+=A[j];
				listA.add(sum);
			}
		}
		listA.sort(null);
		
		for(int i=0; i<M; i++) {
			sum= B[i];
			listB.add(sum);
			for(int j=i+1; j<M; j++) {
				sum+=B[j];
				listB.add(sum);
			}
		}
		listB.sort(null);
		
		//계산 및 출력
		long cnt=0;
		int left, right, mid, lb, ub;
		for(int i=0; i<listA.size(); i++) {
			//lower
			left=0;
			right=listB.size();
			while(left<right) {
				mid=(left+right)/2;
				if(listA.get(i)+listB.get(mid)>=T){
					right= mid;
				}else {
					left= mid+1;
				}
			}
			lb= left;
			
			//upper
			left=0;
			right=listB.size();
			while(left<right) {
				mid=(left+right)/2;
				if(listA.get(i)+listB.get(mid)<=T){
					left= mid+1;
				}else {
					right= mid;
				}
			}
			ub= left;
			cnt+=ub-lb;
		}//for
		
		System.out.println(cnt);
	}//main
}

https://www.acmicpc.net/problem/2143

 

2143번: 두 배열의 합

첫째 줄에 T(-1,000,000,000 ≤ T ≤ 1,000,000,000)가 주어진다. 다음 줄에는 n(1 ≤ n ≤ 1,000)이 주어지고, 그 다음 줄에 n개의 정수로 A[1], …, A[n]이 주어진다. 다음 줄에는 m(1 ≤ m ≤ 1,000)이 주어지고, 그

www.acmicpc.net

 

[문제]

수정이는 어린 동생을 달래기 위해서 사탕을 사용한다. 수정이는 평소에 여러 개의 사탕을 사서 사탕상자에 넣어두고, 동생이 말을 잘 들을 때면 그 안에서 사탕을 꺼내서 주곤 한다.

각각의 사탕은 그 맛의 좋고 나쁨이 1부터 1,000,000까지의 정수로 구분된다. 1이 가장 맛있는 사탕을 의미하며, 1,000,000은 가장 맛없는 사탕을 의미한다. 수정이는 동생이 말을 잘 들은 정도에 따라서, 사탕상자 안에 있는 사탕들 중 몇 번째로 맛있는 사탕을 꺼내주곤 한다. 예를 들어 말을 매우 잘 들었을 때에는 사탕상자에서 가장 맛있는 사탕을 꺼내주고, 말을 조금 잘 들었을 때에는 사탕상자에서 여섯 번째로 맛있는 사탕을 꺼내주는 식이다.

수정이가 보관하고 있는 사탕은 매우 많기 때문에 매번 사탕상자를 뒤져서 꺼낼 사탕을 골라내는 일은 매우 어렵다. 수정이를 도와주는 프로그램을 작성하시오.

[입력]

첫째 줄에 수정이가 사탕상자에 손을 댄 횟수 n(1≤n≤100,000)이 주어진다. 다음 n개의 줄에는 두 정수 A, B, 혹은 세 정수 A, B, C가 주어진다. A가 1인 경우는 사탕상자에서 사탕을 꺼내는 경우이다. 이때에는 한 정수만 주어지며, B는 꺼낼 사탕의 순위를 의미한다. 이 경우 사탕상자에서 한 개의 사탕이 꺼내지게 된다. 또, A가 2인 경우는 사탕을 넣는 경우이다. 이때에는 두 정수가 주어지는데, B는 넣을 사탕의 맛을 나타내는 정수이고 C는 그러한 사탕의 개수이다. C가 양수일 경우에는 사탕을 넣는 경우이고, 음수일 경우에는 빼는 경우이다. 맨 처음에는 빈 사탕상자에서 시작한다고 가정하며, 사탕의 총 개수는 2,000,000,000을 넘지 않는다. 또한 없는 사탕을 꺼내는 경우와 같은 잘못된 입력은 주어지지 않는다.

[출력]

A가 1인 모든 입력에 대해서, 꺼낼 사탕의 맛의 번호를 출력한다. 물론, A=2 이면서 C<0 일 때는 출력하지 않는다.

[예제]

 

 


>문제 이해

설명)) 사탕의 맛은 좋고 나쁨이 1~ 1,000,000의 정수로 구분된다.

= 나쁨수치 (숫자가 작을수록 맛있고, 높을수록 맛없음)

input))

N: 수정이가 사탕상자에 손을 댄 횟수

N개의 줄에는) A, B 또는 A, B, C가 주어진다.

1) A==1이면? 상자에서 사탕 꺼내는

B=꺼낼 사탕의 순위

2) A==2이면? 사탕을 넣는 경우

B=넣을 사탕의 맛

C=사탕 개수 (음수면 뺄셈)

사탕의 총 개수는 2,000,000,000을 넘지 않는다.

잘못된 입력은 주어지지 않는다.

output))

A가 1인 모든 입력에 대해: 꺼낼 사탕의 맛의번호 출력

A가 2이면서 C<0이면 출력X

>문제 풀이

1. 인덱스의 따른 값이 계속해서 바뀐다.

2. 순위 정보가 필요한데 값이 계속 바뀐다. 범위값이 백만이라 누적합을 매번 구한다면 시간초과가 남.

=> 인덱스 트리를 사용한다.

그래서 포화 이진 트리를 만들어야 하는데, 백만 이상의 2의 제곱수 만큼의 트리를 만들어야한다.

왜냐면, 위처럼 포화 이진 트리를 만들 때, 만약 리프노드가 8개이면 총 15개의 공간이 필요한데, 노드의 번호를 편하게 쓰려면 0을 제외하고 15개의 공간을 만들어야함. 즉 2*leaf=16 공간이 필요하게된다.

이 문제에서는 사탕의 맛의 정도가 1~1,000,000 라서 리프 노드만 백만개이다.

트리를 만들 때는 완전 이진 트리를 만들어야 계산할 수 있으므로 백만이 넘는 2의 제곱수가 필요하다.

* 트리 구조에서 부모노드와 자식노드의 번호의 관계

parent= left/2 (또는 right/2);

left= parent*2;

right= parent*2+1;

 

 

>전체 코드

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;


public class Main {
	static int tree[];
	static int N, S, A, B, C;
	
	public static void main(String[] args) throws IOException {
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		
        //포화 이진트리 생성을 위한 2^n>1000000인 S(=2^n) 찾기
		S=1;
		while(S<1000000) {
			S*=2;
		}
		tree= new int[S*2];
		
		N= Integer.parseInt(br.readLine());
		StringBuilder sb= new StringBuilder();
		for(int i=0; i<N; i++) {
			st= new StringTokenizer(br.readLine());
			A= Integer.parseInt(st.nextToken());
			if(A==1) {
				B= Integer.parseInt(st.nextToken());
				//사탕 꺼내기, B번째
				int index= pickup(1, S, 1, B);
				sb.append(index+"\n");
				update(1, S, 1, index, -1);
			}else {
				B= Integer.parseInt(st.nextToken());
				C= Integer.parseInt(st.nextToken());
				//사탕 넣기 B맛 C개(양수+, 음수-)
				update(1, S, 1, B, C);
			}
		}
		System.out.println(sb.toString());
	}//main
	
    //left, right는 범위(1~S), node: 현재 위치한 노드의 인덱스, target: 목표 노드
	public static int pickup(int left, int right, int node, int target) {
		if(left==right) return left;
		
		int mid= (left+right)/2;
		if(tree[node*2]>=target) { //왼쪽 노드이면 (target그대로)
			return pickup(left, mid, node*2, target);
		}else { //오른쪽 노드이면 (target-왼쪽 노드)
			return pickup(mid+1, right, node*2+1, target-tree[node*2]);
		}
	}
	
    //(left, right, node, target, diff: 더하거나 뺄 사탕개수)
	public static void update(int left, int right, int node, int target, int diff) {
		//타겟이 범위값 벗어나면 종료
		if(target<left||right<target) return;
		
		tree[node]+=diff;
		if(left!=right) {
			int mid= (left+right)/2;
			update(left, mid, node*2, target, diff);
			update(mid+1, right, node*2+1, target, diff);
		}
	}	
}

https://www.acmicpc.net/problem/2243

 

2243번: 사탕상자

첫째 줄에 수정이가 사탕상자에 손을 댄 횟수 n(1≤n≤100,000)이 주어진다. 다음 n개의 줄에는 두 정수 A, B, 혹은 세 정수 A, B, C가 주어진다. A가 1인 경우는 사탕상자에서 사탕을 꺼내는 경우이다.

www.acmicpc.net

 

[문제]

대학 교수인 당신은, 상호평가를 통하여 학생들이 제출한 과제물에 학점을 부여하려고 합니다. 아래는 0번부터 4번까지 번호가 매겨진 5명의 학생들이 자신과 다른 학생의 과제를 평가한 점수표입니다.

No. 0 1 2 3 4
0 100 90 98 88 65
1 50 45 99 85 77
2 47 88 95 80 67
3 61 57 100 80 65
4 24 90 94 75 65
평균 45.5 81.25 97.2 81.6 67.8
학점 F B A B D

위의 점수표에서, i행 j열의 값은 i번 학생이 평가한 j번 학생의 과제 점수입니다.

  • 0번 학생이 평가한 점수는 0번 행에담긴 [100, 90, 98, 88, 65]입니다.
    • 0번 학생은 자기 자신에게 100점, 1번 학생에게 90점, 2번 학생에게 98점, 3번 학생에게 88점, 4번 학생에게 65점을 부여했습니다.
  • 2번 학생이 평가한 점수는 2번 행에담긴 [47, 88, 95, 80, 67]입니다.
    • 2번 학생은 0번 학생에게 47점, 1번 학생에게 88점, 자기 자신에게 95점, 3번 학생에게 80점, 4번 학생에게 67점을 부여했습니다.

당신은 각 학생들이 받은 점수의 평균을 구하여, 기준에 따라 학점을 부여하려고 합니다.
만약, 학생들이 자기 자신을 평가한 점수가 유일한 최고점 또는 유일한 최저점이라면 그 점수는 제외하고 평균을 구합니다.

  • 0번 학생이 받은 점수는 0번 열에 담긴 [100, 50, 47, 61, 24]입니다. 자기 자신을 평가한 100점은 자신이 받은 점수 중에서 유일한 최고점이므로, 평균을 구할 때 제외합니다.
    • 0번 학생의 평균 점수는 (50+47+61+24) / 4 = 45.5입니다.
  • 4번 학생이 받은 점수는 4번 열에 담긴 [65, 77, 67, 65, 65]입니다. 자기 자신을 평가한 65점은 자신이 받은 점수 중에서 최저점이지만 같은 점수가 2개 더 있으므로, 유일한 최저점이 아닙니다. 따라서, 평균을 구할 때 제외하지 않습니다.
    • 4번 학생의 평균 점수는 (65+77+67+65+65) / 5 = 67.8입니다.

제외할 점수는 제외하고 평균을 구한 후, 아래 기준에 따라 학점을 부여합니다.

평균학점

90점 이상 A
80점 이상 90점 미만 B
70점 이상 80점 미만 C
50점 이상 70점 미만 D
50점 미만 F

학생들의 점수가 담긴 정수형 2차원 배열 scores가 매개변수로 주어집니다. 이때, 학생들의 학점을 구하여 하나의 문자열로 만들어서 return 하도록 solution 함수를 완성해주세요.


제한사항

  • 2 ≤ scores의 행의 길이(학생 수) ≤ 10
  • scores의 열의 길이 = scores의 행의 길이
    • 즉, scores는 행과 열의 길이가 같은 2차원 배열입니다.
  • 0 ≤ scores의 원소 ≤ 100
  • return 값 형식
    • 0번 학생의 학점부터 차례대로 이어 붙인 하나의 문자열을 return 합니다.

입출력 예

scoresresult

[[100,90,98,88,65],[50,45,99,85,77],[47,88,95,80,67],[61,57,100,80,65],[24,90,94,75,65]] "FBABD"
[[50,90],[50,87]] "DA"
[[70,49,90],[68,50,38],[73,31,100]] "CFD"

입출력 예 설명

입출력 예 #1

문제 예시와 같습니다.

 

입출력 예 #2

No. 0 1
0 50 90
1 50 87
평균 50 90
학점 D A
  • 1번 학생이 자기 자신을 평가한 87점은 [90, 87]에서 유일한 최저점이므로, 평균을 구할 때 제외합니다.

입출력 예 #3

No. 0 1 2
0 70 49 90
1 68 50 38
2 73 31 100
평균 70.33… 40 64
학점 C F D
  • 1번 학생이 자기 자신을 평가한 50점은 [49, 50, 31]에서 유일한 최고점이므로, 평균을 구할 때 제외합니다.
  • 2번 학생이 자기 자신을 평가한 100점은 [90, 38, 100]에서 유일한 최고점이므로, 평균을 구할 때 제외합니다.

 


>문제 풀이

 프로그래머스 위클리 문제를 풀어보는 중인데 오늘은 2주차 문제를 풀어보았습니다.

레벨 1이라 쉬운 문제였습니다.

방법으로는 여러가지가 있겠지만, 어차피 입력값이 크지도 않고 해서 짧게 짜보았습니다.

방법으로는

1) scores[] 배열을 만들어서 열을 넣은 다음 정렬 -> 최소값이 자기평가 값과 같으면서 유일하거나 최대값이 자기평가 값과 같으면서 유일하면 sum에서 자기평가값을 빼주고 len-1로 나눠 avg를 구해줍니다.

조건에 해당되지 않으면 avg=sum/len;

2) min, max에 해당열의 최소, 최대값을 담고, 자기평가 값과 같은 값이 몇개인지 cnt로 카운팅해서 조건을 판별합니다. min==자기평가 값이고 cnt==1 이거나, max==자기평가 값이면서 cnt==1이면 sum-=scores[i][i]; avg=sum/(len-1)을 해줍니다.

조건에 해당되지 않으면 avg=sum/len;

근데, 적어놓고보니 2번이 더 빠를 것 같습니다..ㅎㅎ;

 

>전체 코드

 

import java.util.Arrays;
class Solution {
    public String solution(int[][] scores) {
        String answer = "";
        int sum, avg, len= scores.length;
        int arr[]= new int[len];
        
        //자기가 받은 점수중에 자기평가 점수가 최고점 or 최저점이면 제외
        //단 유일한 최고점 or 최저점이 아니라 동점이 있으면 제외안함
        for(int i=0; i<len; i++){
            sum=0;
            for(int j=0; j<len; j++){
                arr[j]= scores[j][i];
                sum+= arr[j];
            }
            Arrays.sort(arr);
            avg= sum/len;
            if((arr[0]==scores[i][i]&&arr[0]!=arr[1])||
               (arr[len-1]==scores[i][i]&&arr[len-1]!=arr[len-2])){
                sum-=scores[i][i];
                avg= sum/(len-1);
            }
            if(avg>=90) answer+="A";
            else if(avg>=80) answer+="B";
            else if(avg>=70) answer+="C";
            else if(avg>=50) answer+="D";
            else answer+="F";
        }
        
        return answer;
    }
}

https://programmers.co.kr/learn/courses/30/lessons/83201

 

코딩테스트 연습 - 2주차_상호평가

[[100,90,98,88,65],[50,45,99,85,77],[47,88,95,80,67],[61,57,100,80,65],[24,90,94,75,65]] "FBABD" [[70,49,90],[68,50,38],[73,31,100]] "CFD"

programmers.co.kr

[문제]

김형택은 지금 몰래 Spider Solitaire(스파이더 카드놀이)를 하고 있다. 형택이는 이 게임을 이길 때도 있었지만, 질 때도 있었다. 누군가의 시선이 느껴진 형택이는 게임을 중단하고 코딩을 하기 시작했다. 의심을 피했다고 생각한 형택이는 다시 게임을 켰다. 그 때 형택이는 잠시 코딩을 하는 사이에 자신의 게임 실력이 눈에 띄게 향상된 것을 알았다.

이제 형택이는 앞으로의 모든 게임에서 지지 않는다. 하지만, 형택이는 게임 기록을 삭제 할 수 없기 때문에, 자신의 못하던 예전 기록이 현재 자신의 엄청난 실력을 증명하지 못한다고 생각했다.

게임 기록은 다음과 같이 생겼다.

- 게임 횟수 : X

- 이긴 게임 : Y (Z%)

- Z는 형택이의 승률이고, 소수점은 버린다. 예를 들어, X=53, Y=47이라면, Z=88이다.

X와 Y가 주어졌을 때, 형택이가 게임을 최소 몇 번 더 해야 Z가 변하는지 구하는 프로그램을 작성하시오.

 

[입력]

각 줄에 정수 X와 Y가 주어진다.

 

[출력]

첫째 줄에 형택이가 게임을 최소 몇 판 더 해야하는지 출력한다. 만약 Z가 절대 변하지 않는다면 -1을 출력한다.

 

[제한사항]

1 ≤ X ≤ 1,000,000,000

0 ≤ Y ≤ X

[예제]


>문제 풀이

 

>문제 이해

게임횟수: X

이긴 게임: Y(Z%)

Z는 형택이의 승률, 소수점은 버린다.

ex) X=53, Y=47 → Z =47/53*100=88.xx → 88

input))

X(10억이하), Y

output))

형택이가 최소 몇판을 더 해야하는지 출력

단, 만약 Z가 절대 변하지 않는다면 -1 출력


>문제 풀이

절대 Z가 변하지 않는 경우는??

1) 일단, Z가 100이면 101%로 올라갈 수 없다.

2) Z가 99이면? 이미 전적에 패배가 있기 때문에 몇경기를 더 이기든 100%로 올라갈 수 없다.

위의 경우를 제외하고는 탐색을 하면되는데

우리가 구하려고 하는 값을 answer라고 할때 answer=1~X까지의 수이다.

- 왜?)) 최소 한 경기 이상은 해야 승률이 오를것이고

Z가 98%의 경우 (98+100)/200*100=99퍼로 확률이 올라가기 때문이다.

이제 어떤 방법으로 탐색을 할지 정해야한다.

1~X까지 탐색을 하려면 최악의 경우 10억번을 탐색해야한다. (시간복잡도 O(N))

그러나 이분탐색을 하게 된다면 O(logN) 만에 탐색이 가능하다.

여기까지 왔다면 이분탐색을 구현하면되는데

나는 1) while의 조건문과 (left<=right) (left<right)

2) left=mid+1;, right= mid-1인가 mid로 해야되나 여기서 헷갈렸다..

이전에 이분탐색 할 때도 헷갈렸었는데, 왜 그렇게 되는지 확실히 인지하지 못한채 넘겼어서 그런 것 같다.

1)번의 경우 Z=98일 때는 위에 설명했던 것 처럼 left를 쭉쭉 올려서 right까지 가야하는데 mid=(left+right)/2로 계산하기 때문에 mid에 right값도 들어가려면 left==right일 때도 고려를 해야하기 때문이다.

그래서 left<=right 여야 한다. (코드에 따라 다를 수 있음)

2)번의 경우

초기값 설정할 때도 left=1, right=X로 mid로 올수 있는 값들의 범위를 설정해준거나 다름 없기 때문에 while안에서 left, right 값을 조정할 때도 mid가 올 수 있는 값만 고려해주면 된다.

mid로 계산했을 때 확률이 Z보다 높으면 다음에 고려할 값은 mid 아래의 값들만 고려해주면 되기 때문에 right=mid-1로 조정해주면 된다.

 

>전체 코드

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.io.IOException;

public class Main {
	
	public static void main(String [] args) throws IOException {
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st= new StringTokenizer(br.readLine());
		
		long answer=-1;
		long X= Long.parseLong(st.nextToken());
		long Y= Long.parseLong(st.nextToken());
		int Z= (int)(Y*100/X);
		
		//Z==100: 100퍼에서 101퍼로 못올림
		//Z==99: 전적에 이미 패가 있기 때문에 어떤 방법을 써도 100퍼는 못만든다.
		//이분탐색 logN
		long left=1, right=X, mid=0;
		int nZ;
		while(left<=right) {
			if(Z>=99) {
				answer=-1;
				break;
			}
			mid= (left+right)/2;
			nZ= (int)((Y+mid)*100/(X+mid));
			if(nZ<=Z) {
				left=mid+1;
			}else {
				answer= mid;
				right=mid-1;
			}
		}
		System.out.println(answer);
	}//main
}

https://www.acmicpc.net/problem/1072

 

1072번: 게임

김형택은 지금 몰래 Spider Solitaire(스파이더 카드놀이)를 하고 있다. 형택이는 이 게임을 이길 때도 있었지만, 질 때도 있었다. 누군가의 시선이 느껴진 형택이는 게임을 중단하고 코딩을 하기 시

www.acmicpc.net

+ Recent posts