[문제]

어떤 N개의 수가 주어져 있다. 그런데 중간에 수의 변경이 빈번히 일어나고 그 중간에 어떤 부분의 합을 구하려 한다. 만약에 1,2,3,4,5 라는 수가 있고, 3번째 수를 6으로 바꾸고 2번째부터 5번째까지 합을 구하라고 한다면 17을 출력하면 되는 것이다. 그리고 그 상태에서 다섯 번째 수를 2로 바꾸고 3번째부터 5번째까지 합을 구하라고 한다면 12가 될 것이다.

 

[입력]

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 횟수이고, K는 구간의 합을 구하는 횟수이다. 그리고 둘째 줄부터 N+1번째 줄까지 N개의 수가 주어진다. 그리고 N+2번째 줄부터 N+M+K+1번째 줄까지 세 개의 정수 a, b, c가 주어지는데, a가 1인 경우 b(1 ≤ b ≤ N)번째 수를 c로 바꾸고 a가 2인 경우에는 b(1 ≤ b ≤ N)번째 수부터 c(b ≤ c ≤ N)번째 수까지의 합을 구하여 출력하면 된다.

입력으로 주어지는 모든 수는 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.

 

[출력]

첫째 줄부터 K줄에 걸쳐 구한 구간의 합을 출력한다. 단, 정답은 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.

 

[예제]

 


>문제 풀이

인덱스 트리를 이용하여 풀었습니다.

처음 접하는 알고리즘으로 푸느라 사실 아직 이해를 제대로 했는지 모르겠습니다.

응용 문제가 많다고 하니 개념을 좀 더 살펴 본 후 관련 문제들을 더 풀어보면 좋을 것 같습니다.

인덱스 트리는 구간합을 구해야할 때, 중간에 인덱스의 값이 변하는 상황에서 쓰입니다.

배열로도 구간합을 저장할 수 있지만, 이 경우 특정 인덱스에 담긴 값을 바꿀 경우 배열 전체를 갈아엎어야 합니다..

그래서 이런 경우는 인덱스 트리를 사용하면 되는데, 포화 이진트리의 리프노드에 배열로 받은 값을 넣고 그 위로 구간합을 구하면서 올리는 구조입니다.

처음에 트리를 만들기 위해서는 Bottom-up으로 리프노드부터 위로 올라가는 방법을 쓰는데, 이후 query와(구간합 구하는 메소드) update에서는(특정 인덱스에 담긴 값을 변경) Top-down과 Bottom-up 2가지 방식으로 구현할 수 있습니다.

(알고리즘 수업에서 두 가지 방법으로 모두 구현해 보았지만, 이 문제에서는 top-down으로 제출하였습니다.)

**구현

1. N개의 배열값을 받았다면, 이제 인덱스 트리에 저장해야합니다.

인덱스 트리의 노드 개수는 2의 제곱수-1개 이고 리프 노드에 N개의 값을 저장해야 합니다.

근데 노드 번호는 0부터 시작하는데 연산을 0부터 하도록 구현하기 너무 번거롭습니다. 그래서 node[0]은 남겨두고 1부터 2N까지가 우리가 실제 값을 넣고 사용하는 노드입니다. 즉 트리의 인덱스 N~2N-1까지가 리프노드이고, 여기에 nums[N]을 담아야합니다.

=> N보다 크거나 같은 2의 제곱수가 리프노드의 개수 S이고, 2*S가 트리의 노드 개수 입니다.

ex) N=5 이면 S= 8(=2^3)

2. query ( left, right, node, queryLeft, queryRight)

left: 리프노드의 첫번째 인덱스(=1)

right: 리프노드의 마지막 인덱스(=S)

node: 현재 노드의 번호(Top-down으로 구현했으니 처음에 1로 넘긴다.)

queryLeft: 구하고자하는 구간의 왼쪽 인덱스

queryRight: 구하고자하는 구간의 오른쪽 인덱스

분기점을 3개로 나눠서 구현한다.

(1) (left, right): 현재노드가 담고있는 구간합의 구간과 (queryLeft, queryRight)와 범위가 겹치지 않는 경우

(2) (left, right)와 (queryLeft, queryRight)와 딱 맞는 경우

(3) (left, right)와 (queryLeft, queryRight)이 걸쳐있는 경우

:자식 노드들로 내려가서 필요한 값만 올릴 수 있도록 해야함.

: return query(왼쪽부분) + query(오른쪽 부분)

3. update(left, right, node, target, diff)

: 타겟을 찾아가며 타겟을 포함하는 구간합을 담은 노드에 +diff 처리를 해준다.

: diff 은 c - nums[target]

(c는 업데이트 해야할 값 , nums[target]은 업데이트 전의 값)

 

>전체 코드

 

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


public class Main {
	static long nums[], tree[];
	static int N, M, K, S;
	
	public static void main(String[] args) throws IOException {
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st= new StringTokenizer(br.readLine());
		
		N= Integer.parseInt(st.nextToken());
		M= Integer.parseInt(st.nextToken());
		K= Integer.parseInt(st.nextToken());
		nums= new long[N];
		
		for(int i=0; i<N; i++) {
			nums[i]= Long.parseLong(br.readLine());
		}
		
		S=1;
		while(S<N) {
			S*=2;
		}
		tree= new long[S*2];
		
		initBU();
		
		int a, b;
		long c;
		for(int i=0; i<M+K; i++) {
			st= new StringTokenizer(br.readLine());
			a= Integer.parseInt(st.nextToken());
			b= Integer.parseInt(st.nextToken());
			c= Long.parseLong(st.nextToken());
			
			if(a==1) { //1이면 업뎃
				update(1, S, 1, b, c-nums[b-1]);
				nums[b-1]=c;
			}else if(a==2) { //2면 구간합
				System.out.println(query(1, S, 1, b, c));
			}
		}
		
	}//main
	
	static void initBU() {
		//Leaf에 값 반영
		for(int i=0; i<N; i++) {
			tree[S+i]=nums[i];
		}
		//내부노드 채움
		for(int i=S-1; i>0; i--) {
			tree[i]= tree[i*2]+tree[i*2+1];
		}
	}//init
	
	static long query(int left, int right, int node, int queryLeft, long queryRight) {
		// 연관이 없음
		if(right<queryLeft||queryRight<left) {
			return 0;
		}
		// 현재 노드가 쿼리 범위에 포함됨
		else if(queryLeft<=left&&right<=queryRight) {
			return tree[node];
		}
		// 현재 노드가(정확히는 구간이) 쿼리 범위에 포함은 안되는데, 겹침
		else {
			int mid= (left+right)/2;
			long leftResult= query(left, mid, node*2, queryLeft, queryRight);
			long rightResult= query(mid+1, right, node*2+1, queryLeft, queryRight);
			return leftResult+rightResult;
		}
	}//query
	
	static void update(int left, int right, int node, int target, long 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);
		}
	}//update
}

[문제]

세계적인 도둑 상덕이는 보석점을 털기로 결심했다.

상덕이가 털 보석점에는 보석이 총 N개 있다. 각 보석은 무게 Mi와 가격 Vi를 가지고 있다. 상덕이는 가방을 K개 가지고 있고, 각 가방에 담을 수 있는 최대 무게는 Ci이다. 가방에는 최대 한 개의 보석만 넣을 수 있다.

상덕이가 훔칠 수 있는 보석의 최대 가격을 구하는 프로그램을 작성하시오.

 

[입력]

첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000)

다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000)

다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci ≤ 100,000,000)

모든 숫자는 양의 정수이다.

 

[출력]

첫째 줄에 상덕이가 훔칠 수 있는 보석 가격의 합의 최댓값을 출력한다.

 

[예제]

[힌트]

두 번째 예제의 경우 첫 번째 보석을 두 번째 가방에, 세 번째 보석을 첫 번째 가방에 넣으면 된다.


>문제 풀이

주어진 보석들의 무게와 값어치를 Gem(weight, price)를 이용하여 Gem[] gem에 저장해줍니다.

우리는 주어진 가방안에 담을 수 있는 무게의 보석들 중에 가장 높은 값어치의 보석을 담아야 합니다.

보석: 처음에는 무게를 기준으로 정렬을 해야하고, 가방에 담을 수 있는 보석들을 대상으로 다시 값이 가장 높은 보석을 구해줍니다.

가방: 무게 기준으로 오름차순 정렬을 해주고 보석을 담아야합니다. 그래야 가방이 담을 수 있는 무게보다 더 적은 무게의 보석들을 고려할 수 있습니다.

ex) 가방= 3, 10 보석: (3, 40) (5, 30) (7, 15) (10, 20) =>이럴 경우 첫번째 가방에 (3, 40)을 담고, 두번째 가방에 (5, 30)을 담아야 합니다.

1. 보석들을 무게 기준으로 오름차순 정렬을 합니다.

(2차원 배열이라 weight과 price 중에 기준을 정해줘야합니다.)

2. 가방을 무게기준으로 오름차순 정렬을 해줍니다.

3. Priority Queue를 선언해줍니다. 이 때 기준을 정해줘야하는데, 값을 기준으로 할 것입니다.

포인터 gIndex=0;

4. 연산

for(i: 가방) {
     while(포인터를 옮겨주며 N보다 작고, gem[gIndex]가 가방의 무게 i보다 작은) {
          조건의 보석들을 pq에 넣어줍니다.
     }
     pq가 비어있지 않다면 result+=pq.poll().price;
}

 

>전체 코드

 

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {
	
	public static void main(String[] args) {
		Scanner scan= new Scanner(System.in);
		
		int N= scan.nextInt();
		int K= scan.nextInt();
		Gem[] gem= new Gem[N];
		int[] bags= new int[K];
		
		int w, p;
		for(int i=0; i<N; i++) {
			w= scan.nextInt();
			p= scan.nextInt();
			gem[i]= new Gem(w, p);
		}
		
		for(int i=0; i<K; i++) {
			bags[i]= scan.nextInt();
		}
		
		// 가방 오름차순 정렬
		Arrays.sort(bags);
		// 보석 무게순 정렬(ascending)
		Arrays.sort(gem, (g1, g2)-> Integer.compare(g1.weight, g2.weight));
		// 보석 높은 값 기준 힙
		PriorityQueue<Gem> pq= new PriorityQueue<>((g1, g2)-> Integer.compare(g2.price, g1.price));
		
		int gIndex=0;
		long result=0;
		//1. 남은 가방 중 제일 작은 가방을 선택
		for(int i=0; i<bags.length; i++) {
			//2. 선택된 가방에 넣을 수 있는 남은 보석 중 가장 비싼 보석을 선택(1가방, 1보석)
			while(gIndex<N && gem[gIndex].weight<=bags[i]) {
				pq.add(gem[gIndex++]);
			}
			if(!pq.isEmpty()) result+=pq.poll().price;
		}
		
		System.out.println(result);
		
	}//main
	
	static class Gem {
		int weight;
		int price;
		
		public Gem(int weight, int price) {
			this.weight= weight;
			this.price= price;
		}
	}
}

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

 

1202번: 보석 도둑

첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000) 다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000) 다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci

www.acmicpc.net

 

[문제]

10,000 이하의 자연수로 이루어진 길이 N짜리 수열이 주어진다. 이 수열에서 연속된 수들의 부분합 중에 그 합이 S 이상이 되는 것 중, 가장 짧은 것의 길이를 구하는 프로그램을 작성하시오.

[입력]

첫째 줄에 N (10 ≤ N < 100,000)과 S (0 < S ≤ 100,000,000)가 주어진다. 둘째 줄에는 수열이 주어진다. 수열의 각 원소는 공백으로 구분되어져 있으며, 10,000이하의 자연수이다.

[출력]

첫째 줄에 구하고자 하는 최소의 길이를 출력한다. 만일 그러한 합을 만드는 것이 불가능하다면 0을 출력하면 된다.

[예제]

 


>문제 풀이

* 투포인터 문제

주어진 N길이의 배열에 대해 연속되는 숫자의 합이 S이상이 되는 것중 가장 짧은 길이를 구해야합니다.

배열에 대해 인덱스를 나타낼 변수 int left=0, right=0를 선언을 해줍니다.

left=0을 기준으로 right를 오른쪽으로 이동해가는데, 이 때 갱신되는 합이 S이상인지 아닌지에 따라 처리를 해줍니다.

오른쪽의 값들을 더해가면 무조건 이전의 sum보다는 sum+arr[right]가 더 큰 값이 됩니다.

위의 조건에 주의하며, while문과 함께 포인터들을 조작하여, 최소 길이인 len을 구해줍니다.

if ( sum>=S )

len와 right-left+1을 비교하여 더 작은 값으로 len을 갱신해줍니다.

//그리고 left변수를 옮겨줄건데, 그럼 sum에서도 빼줘야하기 때문에

sum-= arr[left++];

else //sum<S

//아직 S보다 작은 수이기 때문에 더 더해줘야합니다.

//right변수를 오른쪽으로 옮겨주고 sum에 더해줍니다.

sum+= arr[++right]

if ( left와 right범위 체크)

left와 right를 ++ 해줬을 때 범위를 벗어나면 종료시켜야 합니다.

앞에서 arr[N+1]을 해놨기 때문에 +arr[N] 는 정상 동작합니다. 물론 arr[N]값은 0이기 때문에 더했을 때 sum에 영향을 미치지도 않습니다. 하지만 다음 while문을 반복하기 전 계속 left와 right를 조작해도 되는지 범위 체크를 해줘야합니다.

 

 

>전체 코드

 

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

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());
		
		int N, S;
		int arr[];
		
		N= Integer.parseInt(st.nextToken());
		S= Integer.parseInt(st.nextToken());
		arr= new int[N+1];
		
		st= new StringTokenizer(br.readLine());
		for(int i=0; i<N; i++) {
			arr[i]= Integer.parseInt(st.nextToken());
		}
		
		
		int left=0, right=0, sum=arr[0];
		int len=Integer.MAX_VALUE;
		
		while(left<N&&right<N) {
			if(sum<S) {
				sum+=arr[++right];
			}else {
				len= Math.min(len, right-left+1);
				sum-=arr[left++];
			}
			if(left>=N||right>=N) break;
		}
		
		if(len==Integer.MAX_VALUE) len=0;
		System.out.println(len);
	}
}

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

 

1806번: 부분합

첫째 줄에 N (10 ≤ N < 100,000)과 S (0 < S ≤ 100,000,000)가 주어진다. 둘째 줄에는 수열이 주어진다. 수열의 각 원소는 공백으로 구분되어져 있으며, 10,000이하의 자연수이다.

www.acmicpc.net

 

[문제]

사악한 암흑의 군주 이민혁은 드디어 마법 구슬을 손에 넣었고, 그 능력을 실험해보기 위해 근처의 티떱숲에 홍수를 일으키려고 한다. 이 숲에는 고슴도치가 한 마리 살고 있다. 고슴도치는 제일 친한 친구인 비버의 굴로 가능한 빨리 도망가 홍수를 피하려고 한다.

티떱숲의 지도는 R행 C열로 이루어져 있다. 비어있는 곳은 '.'로 표시되어 있고, 물이 차있는 지역은 '*', 돌은 'X'로 표시되어 있다. 비버의 굴은 'D'로, 고슴도치의 위치는 'S'로 나타내어져 있다.

매 분마다 고슴도치는 현재 있는 칸과 인접한 네 칸 중 하나로 이동할 수 있다. (위, 아래, 오른쪽, 왼쪽) 물도 매 분마다 비어있는 칸으로 확장한다. 물이 있는 칸과 인접해있는 비어있는 칸(적어도 한 변을 공유)은 물이 차게 된다. 물과 고슴도치는 돌을 통과할 수 없다. 또, 고슴도치는 물로 차있는 구역으로 이동할 수 없고, 물도 비버의 소굴로 이동할 수 없다.

티떱숲의 지도가 주어졌을 때, 고슴도치가 안전하게 비버의 굴로 이동하기 위해 필요한 최소 시간을 구하는 프로그램을 작성하시오.

고슴도치는 물이 찰 예정인 칸으로 이동할 수 없다. 즉, 다음 시간에 물이 찰 예정인 칸으로 고슴도치는 이동할 수 없다. 이동할 수 있으면 고슴도치가 물에 빠지기 때문이다.

[입력]

첫째 줄에 50보다 작거나 같은 자연수 R과 C가 주어진다.

다음 R개 줄에는 티떱숲의 지도가 주어지며, 문제에서 설명한 문자만 주어진다. 'D'와 'S'는 하나씩만 주어진다.

[출력]

첫째 줄에 고슴도치가 비버의 굴로 이동할 수 있는 가장 빠른 시간을 출력한다. 만약, 안전하게 비버의 굴로 이동할 수 없다면, "KAKTUS"를 출력한다.

​[예제]

 


>문제 풀이

D : 비버 굴

S : 고슴도치 위치

* : 물

. : 빈 곳

X : 돌

char mat[][]에 입력값들을 저장하면서, 물의 위치와 시작 위치를 큐에 담아줍니다.

물의 위치를 먼저 쭉 담고, 시작 위치를 담도록 구현했는데 물 먼저 bfs를 해주고 고슴도치가 움직여야 정답값을 효율적으로 구할 수 있기 때문입니다.

(고슴도치가 움직여도 그 다음에 물에 잠기면 처리가 좀 더 복잡해집니다.)

물이 상하좌우로 확장해나갈 때는 mat범위 내에서 발생하며 값도 *으로 변경해줍니다.

고슴도치는 빈 곳으로만 이동해서 D에 도달해야합니다. 최종적으로 D에 도달했을 때, 몇 분 걸렸는지를 출력해야하는데 dp[][]값에 시작 위치부터 카운트해서 최단거리로 D에 도달하는 시간을 구했습니다.

위의 과정을 다 돌렸는데도, D에 도달하지 못했다면 "KAKTUS"를 출력합니다.


저는 처음에 고슴도치가 먼저가고 물이 확장되도록 구현했는데, 이렇게 했더니 queue.size()만큼만 for문을 반복하게 하는데에서 어려움을 겪었습니다.

 

https://arinnh.tistory.com/40?category=934581 

 

[프로그래머스] level 2: 가장 먼 노드_Java

[문제 설명] n개의 노드가 있는 그래프가 있습니다. 각 노드는 1부터 n까지 번호가 적혀있습니다. 1번 노드에서 가장 멀리 떨어진 노드의 갯수를 구하려고 합니다. 가장 멀리 떨어진 노드란 최단

arinnh.tistory.com

이 문제를 풀었던 것처럼 풀어보려고 했는데, 잘 안되어서 코드를 수정했습니다.

 

 

>전체 코드

 

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
	static int R, C;
	static char[][] mat;
	static int[][] dp;
	static Queue<Point> queue;
	
	static int time=0;
	
	public static void main(String[] args) {
		Point start = null;
		String str="";
		
		Scanner scan= new Scanner(System.in);
		
		R= scan.nextInt();
		C= scan.nextInt();
		mat= new char[R][C];
		dp= new int[R][C];
		queue= new LinkedList<>();
		
		//빈 곳: . // 물: * // 돌: X //비버 굴: D // 고슴도치: S
		for(int i=0; i<R; i++) {
			str= scan.next();
			for(int j=0; j<C; j++) {
				mat[i][j]= str.charAt(j);
				if(mat[i][j]=='S') {
					start= new Point(i, j, 'S');
				}else if(mat[i][j]=='*') {
					queue.add(new Point(i, j, '*'));
				}
			}
		}
		queue.add(start);
		
		bfs();
	}//main
	
	
	static int[] dx= {-1, 0, 1, 0};
	static int[] dy= {0, -1, 0, 1};
	
	//고슴도치가 비버 굴로 가야함(순서:물->고슴도치)
	public static void bfs() {
		Point p;
		int nx, ny;
		
		while(!queue.isEmpty()) {
			p= queue.poll();
			if(p.type=='D') {
				System.out.println(dp[p.x][p.y]);
				return;
			}

			for(int i=0; i<4; i++) {
				nx= p.x+dx[i];
				ny= p.y+dy[i];

				if(!check(nx, ny)) continue;				
				if(p.type=='*') {
					if(mat[nx][ny]=='.'||mat[nx][ny]=='S') {
					mat[nx][ny]= '*';
					queue.add(new Point(nx, ny, '*'));
					}
				}else { //p.type=='.'
					if(mat[nx][ny]=='.'||mat[nx][ny]=='D') {
						if(dp[nx][ny]>0) continue;
						dp[nx][ny]= dp[p.x][p.y]+1;
						queue.add(new Point(nx, ny, mat[nx][ny]));
					}
				}
			}
		}//while
		System.out.println("KAKTUS");
	}

	//범위 확인
	public static boolean check(int x, int y) {
		if(x<0||x>=R) return false;
		if(y<0||y>=C) return false;
		return true;
	}
	
	static class Point{
		int x, y;
		char type;
		public Point(int x, int y, char type) {
			this.x= x;
			this.y= y;
			this.type= type;
		}
	}
}

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

 

3055번: 탈출

사악한 암흑의 군주 이민혁은 드디어 마법 구슬을 손에 넣었고, 그 능력을 실험해보기 위해 근처의 티떱숲에 홍수를 일으키려고 한다. 이 숲에는 고슴도치가 한 마리 살고 있다. 고슴도치는 제

www.acmicpc.net

 

[문제 설명]

1부터 n까지 번호가 붙어있는 n명의 사람이 영어 끝말잇기를 하고 있습니다. 영어 끝말잇기는 다음과 같은 규칙으로 진행됩니다.

1번부터 번호 순서대로 한 사람씩 차례대로 단어를 말합니다.

마지막 사람이 단어를 말한 다음에는 다시 1번부터 시작합니다.

앞사람이 말한 단어의 마지막 문자로 시작하는 단어를 말해야 합니다.

이전에 등장했던 단어는 사용할 수 없습니다.

한 글자인 단어는 인정되지 않습니다.

다음은 3명이 끝말잇기를 하는 상황을 나타냅니다.

tank → kick → know → wheel → land → dream → mother → robot → tank

위 끝말잇기는 다음과 같이 진행됩니다.

1번 사람이 자신의 첫 번째 차례에 tank를 말합니다.

2번 사람이 자신의 첫 번째 차례에 kick을 말합니다.

3번 사람이 자신의 첫 번째 차례에 know를 말합니다.

1번 사람이 자신의 두 번째 차례에 wheel을 말합니다.

(계속 진행)

끝말잇기를 계속 진행해 나가다 보면, 3번 사람이 자신의 세 번째 차례에 말한 tank 라는 단어는 이전에 등장했던 단어이므로 탈락하게 됩니다.

사람의 수 n과 사람들이 순서대로 말한 단어 words 가 매개변수로 주어질 때, 가장 먼저 탈락하는 사람의 번호와 그 사람이 자신의 몇 번째 차례에 탈락하는지를 구해서 return 하도록 solution 함수를 완성해주세요.

[제한 사항]

끝말잇기에 참여하는 사람의 수 n은 2 이상 10 이하의 자연수입니다.

words는 끝말잇기에 사용한 단어들이 순서대로 들어있는 배열이며, 길이는 n 이상 100 이하입니다.

단어의 길이는 2 이상 50 이하입니다.

모든 단어는 알파벳 소문자로만 이루어져 있습니다.

끝말잇기에 사용되는 단어의 뜻(의미)은 신경 쓰지 않으셔도 됩니다.

정답은 [ 번호, 차례 ] 형태로 return 해주세요.

만약 주어진 단어들로 탈락자가 생기지 않는다면, [0, 0]을 return 해주세요.

[입출력 예]

n words result
3 ["tank", "kick", "know", "wheel", "land", "dream", "mother", "robot", "tank"] [3,3]
5 ["hello", "observe", "effect", "take", "either", "recognize", "encourage", "ensure", "establish", "hang", "gather", "refer", "reference", "estimate", "executive"] [0,0]
2 ["hello", "one", "even", "never", "now", "world", "draw"] [1,3]

*입출력 예 설명

입출력 예 #1

3명의 사람이 끝말잇기에 참여하고 있습니다.

1번 사람 : tank, wheel, mother

2번 사람 : kick, land, robot

3번 사람 : know, dream, tank

와 같은 순서로 말을 하게 되며, 3번 사람이 자신의 세 번째 차례에 말한 tank라는 단어가 1번 사람이 자신의 첫 번째 차례에 말한 tank와 같으므로 3번 사람이 자신의 세 번째 차례로 말을 할 때 처음 탈락자가 나오게 됩니다.

입출력 예 #2

5명의 사람이 끝말잇기에 참여하고 있습니다.

1번 사람 : hello, recognize, gather

2번 사람 : observe, encourage, refer

3번 사람 : effect, ensure, reference

4번 사람 : take, establish, estimate

5번 사람 : either, hang, executive

와 같은 순서로 말을 하게 되며, 이 경우는 주어진 단어로만으로는 탈락자가 발생하지 않습니다. 따라서 [0, 0]을 return하면 됩니다.

입출력 예 #3

2명의 사람이 끝말잇기에 참여하고 있습니다.

1번 사람 : hello, even, now, draw

2번 사람 : one, never, world

와 같은 순서로 말을 하게 되며, 1번 사람이 자신의 세 번째 차례에 'r'로 시작하는 단어 대신, n으로 시작하는 now를 말했기 때문에 이때 처음 탈락자가 나오게 됩니다.


>문제 풀이

answer[0]은 n명 중에 몇번째 사람이 틀렸는가

answer[1]은 틀린 사람이 몇번째 대답에서 틀렸는가

1) 즉, words[i]번째 글자의 길이가 1이면 break;

2) words[i]번째 값이 이전 단어의 끝 글자로 시작하지 않으면 break;

3) 이미 말한적 있는 단어면 break;

- 중복 여부는 set<Integer>로 체크합니다.

위의 조건에 해당되지 않으면, set.add(words[i]);

조건에 해당되어 나오면 words[i]번째 값이 잘못되었다는 뜻이므로 i를 기준으로 answer[0], answer[1]를 구해줍니다.

단, words[] 값을 다 돌았는데도 틀린 사람이 없으면 [0, 0]을 반환하도록 해줍니다.

 

>전체 코드

 

import java.util.HashSet;
class Solution {
    public int[] solution(int n, String[] words) {
        int[] answer = new int[2];
        HashSet<String> set= new HashSet<>();
        int i;
        
        set.add(words[0]);
        for(i=1; i<words.length; i++){
            if(words[i].length()==1) break;
            if(words[i-1].charAt(words[i-1].length()-1)!=words[i].charAt(0)) break;
            if(set.contains(words[i])) break;
            set.add(words[i]);
        }
        
        answer[0]=(i+1)%n==0?n:(i+1)%n;
        answer[1]=(int)Math.ceil((double)(i+1)/n);
        if(i==words.length) {
            answer[0]=0;
            answer[1]=0;
        }
        
        return answer;
    }
}

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

 

코딩테스트 연습 - 영어 끝말잇기

3 ["tank", "kick", "know", "wheel", "land", "dream", "mother", "robot", "tank"] [3,3] 5 ["hello", "observe", "effect", "take", "either", "recognize", "encourage", "ensure", "establish", "hang", "gather", "refer", "reference", "estimate", "executive"] [0,0]

programmers.co.kr

 

+ Recent posts