문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 256 MB 87846 23673 14467 24.067%

문제

그래프의 정점의 집합을 둘로 분할하여, 각 집합에 속한 정점끼리는 서로 인접하지 않도록 분할할 수 있을 때, 그러한 그래프를 특별히 이분 그래프 (Bipartite Graph) 라 부른다.

그래프가 입력으로 주어졌을 때, 이 그래프가 이분 그래프인지 아닌지 판별하는 프로그램을 작성하시오.

입력

입력은 여러 개의 테스트 케이스로 구성되어 있는데, 첫째 줄에 테스트 케이스의 개수 K가 주어진다. 각 테스트 케이스의 첫째 줄에는 그래프의 정점의 개수 V와 간선의 개수 E가 빈 칸을 사이에 두고 순서대로 주어진다. 각 정점에는 1부터 V까지 차례로 번호가 붙어 있다. 이어서 둘째 줄부터 E개의 줄에 걸쳐 간선에 대한 정보가 주어지는데, 각 줄에 인접한 두 정점의 번호 u, v (u ≠ v)가 빈 칸을 사이에 두고 주어진다.

출력

K개의 줄에 걸쳐 입력으로 주어진 그래프가 이분 그래프이면 YES, 아니면 NO를 순서대로 출력한다.

제한

  • 2 ≤ K ≤ 5
  • 1 ≤ V ≤ 20,000
  • 1 ≤ E ≤ 200,000

풀이

이분 그래프란?

그래프의 정점의 집합을 둘로 분할하여, 각 집합에 속한 정점끼리는 서로 인접하지 않도록 분할할 수 있어야 한다.

나의 경우 bfs 탐색을 통해 depth가 증가할 때 마다 colors 배열의 값을 토글시켜줬다. 만약 현재 정점이 방문 가능한 정점의 인덱스가 colors 배열에 값이 있다면 현재 정점의 인덱스와 비교하여 같은 값을 가지고 있다면 이분 그래프가 아니다.

만약 colors 배열의 새로운 정점의 인덱스 위치에 값이 0이라면, 현재 인덱스의 값을 토글하여 저장한다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static int[] colors;
    static ArrayList<Integer>[] arrayLists;
    static int V;
    static int E;
    static String res;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(br.readLine());

        int testCaseCount =  Integer.parseInt(st.nextToken());
        
        for (int i = 0; i < testCaseCount; i++) {
            st = new StringTokenizer(br.readLine());
            V = Integer.parseInt(st.nextToken());
            E = Integer.parseInt(st.nextToken());
            colors = new int[V];
            arrayLists = new ArrayList[V];
            for (int j = 0; j < V; j++) {
                arrayLists[j] = new ArrayList<>();
            }
            for (int j = 0; j < E; j++) {
                st = new StringTokenizer(br.readLine());
                int first = Integer.parseInt(st.nextToken())-1;
                int second = Integer.parseInt(st.nextToken())-1;
                arrayLists[first].add(second);
                arrayLists[second].add(first);
            }
            res = "YES";
            for (int j = 0; j < V; j++) {
                if (colors[j] == 0)
                    bfs(j);
            }
            sb.append(res).append("\\n");
        }
        System.out.println(sb);
    }
    static void bfs(int v) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(v);
        colors[v] = 1;
        while (!queue.isEmpty()) {
            int val = queue.poll();
            for (int vertex : arrayLists[val]) {
                if (colors[vertex] == 0) { // 0이면 색칠 안된 곳
                    colors[vertex] = colors[val] * -1;
                    queue.add(vertex);
                }
                if (colors[vertex] == colors[val]) {
                    res = "NO";
                    return;
                }
            }
        }
    }
}

개요

C# Application에서 C++ Dll을 호출하기 위해서는 마샬링 과정이 필수적이다. 또한, 마샬링 과정에서 Calling Convention이 조율되지 않으면 스택에서 오류가 발생하는데, 어떻게 이러한 문제가 발생하는 것인지 알아보자.

목표

  1. 마샬링이 무엇인지 설명할 수 있다.
  2. Calling Convention이 무엇인지 설명할 수 있다.

C# Application과 C++ Dll의 통신

마샬링(Marshalling)

마샬링이란 한 객체의 메모리에서의 표현방식을 저장 또한 전송에 적합한 다른 데이터 형식으로 변환하는 과정입니다.

이는 데이터를 서로 다른 프로그램간에 전달할 필요가 있을 경우 사용합니다.

즉, 이는 직렬화와 유사하며 직렬화된 한 객체로, 멀리 떨어진 객체와 통신하기 위해 사용합니다.

이렇듯 복잡한 통신을 단순화하여 쉽게 데이터를 주고받을 수 있도록 해주는 것이 마샬링 입니다.

따라서, Managed Language인 C#과, Unmanaged Language인 C++ 프로그램 간 통신을 위해서는, 어떤 타입으로 데이터를 송수신할 지 마샬링을 통해서 조율할 수 있는 것이다.

마샬링과 직렬화는 무엇이 다른가?

마샬링은 객체의 메모리 구조에서 저장 또는 전송에 적합한 다른 데이터 형식으로 변환하는 과정

  • 마샬링은 프로그램간 이동할 때 사용하는 변환 과정

→ 이 과정에서 비마샬링할 때 객체의 복사본을 얻을 수 있게 원격 객체의 상태와 코드베이스를 기록한다.

직렬화는 객체의 상태를 저장하기 위해 객체를 바이트 스트림(Byte Stream) 형태로 변환하는 것

  • 객체에 저장된 데이터를 스트림 형태로 쓰기위해 연속적인 데이터로 변환하는 것이다

마샬링은 직렬화보다 더 큰 개념으로 사용된다. 그래서 직렬화 가능하거나 리모트 가능한 모든 객체는 마샬링이 가능하다.

→ 큰 차이는 직렬화는 객체가 대상이고 마샬링은 변환 그 자체의 목적으로 코드베이스를 기록하는데 차이가 있다.

함수 호출 규약이란?

호출 규약은 어떻게 서브루틴이 그들의 호출자(caller)로부터 변수를 받고, 어떻게 결과를 반환하는지에 대한 규약이다.

  • 프로그래밍 언어, 플랫폼 마다 각기 다른 호출 규약을 사용한다. (CPU 아키텍쳐 + 운영체제)
  • 여러 언어에서 작성한 모듈을 병합할 때, 또는 작성 장소가 다른 언어의 운영 체제 및 라이브러리 API를 호출할 때 문제를 일으킬 수도 있다
  • 호출자(caller)와 피호출자(callee)가 사용하는 호출 규약을 조율하는데 관심을 가져야 한다.
  • 단일 프로그래밍 언어를 사용하는 프로그램에서 여러 개의 호출 규약을 사용할 수 있다.

왜 이렇게 다양한 함수 호출 규약이 존재하는가?

왜 이렇게 다양한 함수 호출 규약이 존재하는지 궁금하여 Stack Overflow 문서를 확인하던 중 아래의 답변을 발견했다.

호출 규칙은 수십 년에 걸쳐 다양한 언어와 하드웨어에 맞게 설계되었습니다. 그들은 모두 다른 목표를 가지고 있었습니다. cdecl은 printf에 대한 가변 인수를 지원합니다. stdcall로 인해 코드 생성이 더 작아졌지만 가변 인수는 없었습니다. Fastcall은 구형 시스템에서 하나 또는 두 개의 인수만 사용하여 간단한 함수의 성능을 크게 향상시킬 수 있습니다(그러나 오늘날에는 속도가 거의 향상되지 않습니다). x64가 도입되었을 때 적어도 Windows에서는 단일 호출 규칙을 갖도록 설계되었습니다.

즉, 언어와 하드웨어의 발전 및 다른 목표를 위하여 여러개의 Calling Convention(cdecl, stdcall, fastcall…)이 설계되었고, 현대의 64bit 운영체제에서는 단일 호출 규칙을 사용한다고 한다.

그렇다면 현재 사용되고 있는 단일 호출 규약은 무엇일까?

64bit 기본 함수 호출 규약

MS 공식 문서의 x64 calling convention 페이지에 의하면, 현재 기본적으로 사용되는 함수 호출 규약은 __vectorcall 이라는 호출 규약을 사용하는 것으로 보인다. 또한, __vectorcall 문서에 의하면 이 __vectorcall의 경우 __fastcall 호출 규약을 베이스로 만들어진 것으로 보인다.

<aside> 💡 The __vectorcall calling convention specifies that arguments to functions are to be passed in registers, when possible. __vectorcall uses more registers for arguments than [__fastcall](<https://learn.microsoft.com/en-us/cpp/cpp/fastcall?view=msvc-170>) or the default x64 calling convention use.

</aside>

마무리

마샬링과정에서 함수 호출 규약을 조율하지 않는다면 호출자와 피호출자간의 스택 정리 방법이 달라서 문제가 발생할 수 있다. 예를들어 호출자는 __stdcall 호출 규약을 사용하고, 피호출자는 __cdecl 호출 규약을 사용한다면, 호출자도 스택 관리에 관여하고, 피 호출자도 스택 관리에 관여하기 때문에 문제가 발생할 수 밖에 없는 것이다.

Reference

https://hwanine.github.io/network/Marshalling/

https://velog.io/@wlsrhkd4023/CS-마샬링Marshalling

https://stackoverflow.com/questions/3428332/why-are-there-so-many-different-calling-conventions

https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170

https://learn.microsoft.com/en-us/cpp/cpp/vectorcall?view=msvc-170

'TIL' 카테고리의 다른 글

SPOPARTY 서비스 파일 업로드, 삭제 장애 대응  (1) 2024.03.02
Jenkins Container 접속 http 경로 변경하기  (0) 2024.02.28
[키워드 정리] VNC VS RDP, ICMP  (0) 2022.03.09
JavaScript ES6 문법 정리  (0) 2021.10.17
2021.09.11  (0) 2021.09.11

문제

각 변에 다음과 같이 16진수 숫자(0~F)가 적혀 있는 보물상자가 있다.

보물 상자의 뚜껑은 시계방향으로 돌릴 수 있고, 한 번 돌릴 때마다 숫자가 시계방향으로 한 칸씩 회전한다.

각 변에는 동일한 개수의 숫자가 있고, 시계방향 순으로 높은 자리 숫자에 해당하며 하나의 수를 나타낸다.

예를 들어 [Fig.1]의 수는 1A3, B54, 8F9, D66이고, [Fig.2]의 수는 61A, 3B5, 48F, 9D6이다.

보물상자에는 자물쇠가 걸려있는데, 이 자물쇠의 비밀번호는 보물 상자에 적힌 숫자로 만들 수 있는 모든 수 중, K번째로 큰 수를 10진 수로 만든 수이다.

N개의 숫자가 입력으로 주어졌을 때, 보물상자의 비밀 번호를 출력하는 프로그램을 만들어보자.

(서로 다른 회전 횟수에서 동일한 수가 중복으로 생성될 수 있다. 크기 순서를 셀 때 같은 수를 중복으로 세지 않도록 주의한다.)

[제약 사항]

  1. N은 4의 배수이고, 8이상 28이하의 정수이다. (8 ≤ N ≤ 28)
  2. N개의 숫자는 각각 0이상 F이하로 주어진다. (A~F는 알파벳 대문자로만 주어진다.)
  3. K는 생성 가능한 수의 개수보다 크게 주어지지 않는다.

[예제]

아래와 같이 (1, B, 3, B, 3, B, 8, 1, F, 7, 5, E) 12개의 숫자가 주어지고 K가 10인 경우를 살펴보자.

이 경우에 생성 가능한 수는 각 회전 별로 다음과 같다.

0회전 : 1B3, B3B, 81F, 75E1회전 : E1B, 3B3, B81, F752회전 : 5E1, B3B, 3B8, 1F73회전 : 0회전과 동일

생성 가능한 수를 내림 차순으로 나열하면 다음과 같고, K(=10)번째로 큰 수는 503(=1F7)이다.(B3B를 중복으로 세지 않도록 주의한다.)

F75, E1B, B81, B3B, 81F, 75E, 5E1, 3B8, 3B3, 1F7, 1B3

[입력]

가장 첫 줄에는 테스트 케이스의 개수 T가 주어지고, 그 아래로 각 테스트 케이스가 주어진다.

각 테스트 케이스의 첫 번째 줄에는 숫자의 개수 N과 크기 순서 K가 주어 진다.

그 다음 줄에는 16진수 0~F 숫자가 공백 없이 N개 주어진다.

[출력]

출력의 각 줄은 '#t'로 시작하고, 공백을 한 칸 둔 다음 정답을 출력한다.

(t는 테스트 케이스의 번호를 의미하며 1부터 시작한다.)

풀이

소요시간 약 60분

  1. 변의 갯수는 4개로 동일하다.
  2. 회전 횟수는 전체 문자열의 길이 / 변의 갯수 - 1 이다. (만약 문자열의 길이가 16이라면, 변의갯수 4로 나눌 경우 수는 4글자가 나온다. 따라서 3회전까지 수행 후 4회전은 0회전과 동일하다.)
  3. 각 문자열을 잘라서 list에 추가한다. 이 때, 숫자의 비교는 16진수로 변환하여 비교한다. 또한, 같은 문자열이 입력되었을 경우에는 0을 리턴하도록 처리해준다.
  4. k번째 문자열을 16진수로 변환하여 출력한다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;

public class Solution {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new java.io.InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		int t = Integer.parseInt(br.readLine());
		
		for (int i = 1; i <= t; i++) {
			long answer = 0;
			
			String[] input = br.readLine().split(" ");
			int n = Integer.parseInt(input[0]);
			int k = Integer.parseInt(input[1]);
			
			int rotationCount = n / 4;
			

			String strings = br.readLine();
			ArrayList<String> queue = new ArrayList<String>();

			for (int j = 0; j < rotationCount; j++) {
				for (int l = 0; l < strings.length(); l = l + rotationCount) {
					String s = strings.substring(l, l + rotationCount);
					if (!queue.contains(s))
						queue.add(s);
				}
				String copy = strings.substring(0, strings.length()-1);
				strings = strings.charAt(strings.length()-1) + copy;
			}
			queue.sort(new Comparator<String>() {

				@Override
				public int compare(String o1, String o2) {
					if (o1.equals(o2)) return 0;
					long first = 0;
					long second = 0;
					for (int j = o1.length()-1; j >= 0; j--) {
						char c1 = o1.charAt(j);
						char c2 = o2.charAt(j);
						if (Character.isDigit(c1)) {
							first += ((int)c1 - '0') * Math.pow(16, o1.length() -1 - j);
						} else {
							first += ((int)c1 - 'A' + 11) * Math.pow(16, o1.length() -1-j);
						}
						if (Character.isDigit(c2)) {
							second += ((int)c2 - '0') * Math.pow(16, o1.length() -1-j);
						} else {
							second += ((int)c2 - 'A' + 11) * Math.pow(16, o1.length() -1 -j);
						}
					}
					return (int) (second-first);
				}
			});
			String ans = queue.get(k-1);
		
			for (int j = ans.length()-1; j >= 0; j--) {
				char c1 = ans.charAt(j);
				if (Character.isDigit(c1)) {
					answer += (c1 - '0') * Math.pow(16, ans.length() -1 - j);
				} else {
					answer += (c1 - 'A' + 10) * Math.pow(16, ans.length() -1-j);
				}
				
			}
			
			sb.append(String.format("#%d %d\\n", i, answer));
		}
		System.out.println(sb);
		
	}
}

 

문제 출처

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AWXRUN9KfZ8DFAUo&categoryId=AWXRUN9KfZ8DFAUo&categoryType=CODE&problemTitle=%EC%97%AD%EB%9F%89&orderBy=FIRST_REG_DATETIME&selectCodeLang=ALL&select-1=&pageSize=10&pageIndex=1

서버 이중화 개요

모 기업의 CS지식 문제에서 잘 모르는 내용이 있어서 정리하고 기록하기위해 포스팅 합니다.

목표 : HA, 클러스터링, Fail-Over를 이해하고 설명하는 것을 목표로 한다.

HA(High Availability)

고가용성은 서버, 네트워크, 프로그램 등의 정보 시스템이 상당히 오랜 기간 동안 지속적으로 정상 운영이 가능한 성질을 말한다. 고(高)가용성이란 "가용성이 높다"는 뜻으로서, "절대 고장 나지 않음"을 의미한다. 고가용성은 흔히 가용한 시간의 비율을 99%, 99.9% 등과 같은 퍼센티지로 표현하는데, 1년에 계획된 것 제외 5분 15초 이하의 장애시간을 허용한다는 의미의 파이브 나인스(5 nines), 즉 99.999%는 매우 높은 수준으로 고품질의 데이터센터에서 목표로 한다고 알려져 있다. 하나의 정보 시스템에 고가용성이 요구된다면, 그 시스템의 모든 부품과 구성 요소들은 미리 잘 설계되어야 하며, 실제로 사용되기 전에 완전하게 시험되어야 한다.

HA 솔루션의 종류

  • 클러스터링
  • 서버 이중화
  • RAID
  • scalibity

https://docs.oracle.com/cd/E25054_01/fusionapps.1111/e14496/scale_config.htm

HA와 서버 이중화의 관계

HA는 오랜 기간 동안 지속적으로 정상 운영이 가능한 성질을 말한다. 따라서, HA를 위한 솔루션으로 서버 이중화가 포함될 수 있다.

서버 이중화

서버 이중화란 운영중인 서비스의 안정성을 위해 각종 자원(하드웨어, OS, 미들웨어, DB 등)을 이중 혹은 그 이상으로 구성하는 것을 말한다.

서버 이중화의 목적

  1. 장애 또는 재해시의 빠른 서비스 재개를 위함 (Fail-Over)
  • 하드웨어, 미들웨어 등 다양한 지점에서 오류가 발생할 수 있으며 사용자가 이를 인지하지 못하도록 하기 위함
  • 서비스의 일시적인 중단이 발생하여도 재빠르게 대응하기 위함
  1. 원활한 서비스의 성능을 보장하기 위함 (Load Balancing)
  • 하나의 기기에서 일정량 이상의 사용자 트랜잭션을 처리하는 경우 응답시간이 느려질 가능성 존재
  • 사용 트랜잭션의 패턴과 사용량 등을 분석해 부하를 분산하여 효율적인 업무처리가 가능
  • 부하분산을 구현하고자 하는 지점에 따라 미들웨어, 네트워크, OS 등 다양한 지점에서 구현이 가능

서버 이중화 방법 및 고려요소

서버를 이중화를 구성할 때 Active-Active 또는 Active-StandBy 등으로 구성할 수 있다.

  • Active-Active : 부하분산 등의 목적으로 주로 사용. 서비스 단위를 나누어 분산시키기도 함
  • Active-Stand by : 즉각적인 Failover(장애 대비. 장애가 발생하였을때 예비 시스템으로 동작하는 것)을 위해 주로 구성

클러스터링

각기 다른 서비스를 하나로 묶어서 하나의 시스템같이 동작하게 함으로써 클라이언트에 고 가용성의 서비스를 제공 하는 것.

클러스터 기술은 세가지 유형의 장애를 대비한다.

  1. 어플리케이션과 서비스 장애
  • 어플리케이션과 필수 서비스에 영향을 미치는 경우
  1. 시스템과 하드웨어 장애
  • 하드웨어를 구성하는 CPU, drives, memory, network adapters, 전원공급기에 영향을 미치는 경우
  1. 여러기관의 사이트 장애
  • 자연재해, 정전, 연결 중단 등으로 발생할 수 있다.

→ 클러스터링도 장애에 대비하는 기능(Fail-Over) 기능을 제공한다.

정리

마무리 하자면, 서버의 고 가용성(HA)를 위해서 서버 이중화, 클러스터링과 같은 솔루션을 사용할 수 있으며, 이러한 솔루션은 대게 장애에 대비하는 기능(Fail-Over)를 제공한다.

정리한 내용에서 궁금한 점이나, 수정이 필요한 점은 댓글로 알려주시면 감사하겠습니다.

Reference

https://lipcoder.tistory.com/523#google_vignette

https://docs.oracle.com/cd/E25054_01/fusionapps.1111/e14496/scale_config.htm

https://yoonwould.tistory.com/152

https://velog.io/@ragnarok_code/서버클러스터란

'Computer Science' 카테고리의 다른 글

Docker란?  (1) 2024.02.19
SSL이란?  (0) 2024.02.19
HLS(HTTP 라이브 스트리밍)란?  (0) 2023.12.28

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 512 MB 89082 51323 28549 54.923%

문제

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.

연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다.

일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.

예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.

2 0 0 0 1 1 0
0 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.

2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.

2 1 0 0 1 1 0
1 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 1 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

바이러스가 퍼진 뒤의 모습은 아래와 같아진다.

2 1 0 0 1 1 2
1 0 1 0 1 2 2
0 1 1 0 1 2 2
0 1 0 0 0 1 2
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.

연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 8)

둘째 줄부터 N개의 줄에 지도의 모양이 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 위치이다. 2의 개수는 2보다 크거나 같고, 10보다 작거나 같은 자연수이다.

빈 칸의 개수는 3개 이상이다.

출력

첫째 줄에 얻을 수 있는 안전 영역의 최대 크기를 출력한다.

풀이

3개의 벽을 세워서 얻을 수 있는 안전영역의 크기를 구하는 문제이다.

내가 생각한 풀이 과정은 아래와 같다.

  1. 완전탐색을 이용하여 3개의 벽을 세운다.
  2. 벽의 갯수가 3개일 경우에 초기 바이러스 위치에서부터 bfs 4방 탐색하여 바이러스를 퍼트린다.
  3. 총 배열 크기에서 바이러스의 갯수와 벽의 갯수를 뺀 갯수가 안전 영역의 갯수이므로 매번 bfs 수행 시마다 안전 영역의 갯수를 비교하여 최댓값을 저장한다.

코드

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

public class B14502 {
    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] { 0,0,-1,1};

    static int N;
    static int M;
    static int[][] graph;
    static int[][] tempGraph;
    static ArrayList<int[]> virus = new ArrayList<>();
    static int safeArea;
    static int initWallCount;
    static int initVirusCount;
    static int virusCount;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] input = br.readLine().split(" ");

        N = Integer.parseInt(input[0]);
        M = Integer.parseInt(input[1]);

        graph = new int[N][M];
        tempGraph = new int[N][M];
        for (int i = 0; i < N; i++) {
            String[] arr = br.readLine().split(" ");
            for (int j = 0; j < arr.length; j++) {
                graph[i][j] = Integer.parseInt(arr[j]);
                if (graph[i][j] == 2) {
                    virus.add(new int[]{i, j});
                    initVirusCount++;
                } else if (graph[i][j] == 1) {
                    initWallCount++;
                }
            }
        }
        combi(0);
        System.out.println(safeArea);
    }
    static void copyGraph() {
        for (int i = 0; i < N; i++) {
            System.arraycopy(graph[i],0,tempGraph[i],0,graph[i].length);
        }
    }
    public static void combi(int wallCount) {
        if (wallCount == 3) {
            // bfs 탐색
            copyGraph();
            bfs();
            int count = N*M - (initWallCount + wallCount + initVirusCount + virusCount);
            safeArea = Math.max(count, safeArea);
            virusCount = 0;
            copyGraph();
            return;
        }
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (graph[i][j] == 0) {
                    graph[i][j] = 1;
                    combi(wallCount+1);
                    graph[i][j] = 0;
                }
            }
        }
    }
    public static void bfs() {
        Queue<int[]> queue = new LinkedList<>();
        queue.addAll(virus);
        while (!queue.isEmpty()) {
            int[] v = queue.poll();
            for (int i = 0; i < 4; i++) {
                int cy = v[0] + dy[i];
                int cx = v[1] + dx[i];

                if (cy < 0 || cx < 0 || cy >= N || cx >= M) continue;
                if (tempGraph[cy][cx] != 0) continue;
                tempGraph[cy][cx] = 2;
                virusCount++;
                queue.add(new int[]{cy,cx});
            }
        }
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

1 초 256 MB 37117 9871 6578 24.587%

문제

상근이는 빈 공간과 벽으로 이루어진 건물에 갇혀있다. 건물의 일부에는 불이 났고, 상근이는 출구를 향해 뛰고 있다.

매 초마다, 불은 동서남북 방향으로 인접한 빈 공간으로 퍼져나간다. 벽에는 불이 붙지 않는다. 상근이는 동서남북 인접한 칸으로 이동할 수 있으며, 1초가 걸린다. 상근이는 벽을 통과할 수 없고, 불이 옮겨진 칸 또는 이제 불이 붙으려는 칸으로 이동할 수 없다. 상근이가 있는 칸에 불이 옮겨옴과 동시에 다른 칸으로 이동할 수 있다.

빌딩의 지도가 주어졌을 때, 얼마나 빨리 빌딩을 탈출할 수 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수가 주어진다. 테스트 케이스는 최대 100개이다.

각 테스트 케이스의 첫째 줄에는 빌딩 지도의 너비와 높이 w와 h가 주어진다. (1 ≤ w,h ≤ 1000)

다음 h개 줄에는 w개의 문자, 빌딩의 지도가 주어진다.

  • '.': 빈 공간
  • '#': 벽
  • '@': 상근이의 시작 위치
  • '*': 불

각 지도에 @의 개수는 하나이다.

출력

각 테스트 케이스마다 빌딩을 탈출하는데 가장 빠른 시간을 출력한다. 빌딩을 탈출할 수 없는 경우에는 "IMPOSSIBLE"을 출력한다.

풀이

BFS로 풀 수 있는 문제였다.

  1. 배열을 벽, 불, 상근이, 빈 공간 으로 나눠서 값을 업데이트 해준다.
  2. 움직일 수 있는 사람이 있는지 확인
  3. 현재 시점에 불들을 4 방향으로 불을 퍼뜨린다 (시간이 없을 경우 한번에 불 다붙이고 끝나버림)
  4. 현재 시점에 사람을 4 방향으로 이동시킨다.
  5. 만약 사람이 배열의 끝 부분에 도착했다면 가장 빨리 도착한 경우 업데이트 해준다.

푸는데 중요했던 부분은 두가지가 있었다.

불이 2개가 붙어있다면, 주기마다 2개의 불을 다 이동시켜야 하는 것

빌딩을 탈출하는데에 가장 빠른 시간을 출력하는 것

 

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;

public class B5427 {
    static final int WALL = -2;
    static final int FIRE = -1;
    static final int EMPTY_SPACE = 0;
    static final int PERSON = 1;

    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,-1,1};
    static int[][] graph;
    static Queue<int[]> fires = new LinkedList<>();
    static Queue<int[]> person = new LinkedList<>();
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        int T = Integer.parseInt(br.readLine());

        for (int i = 0; i < T; i++) {
            String[] size = br.readLine().split(" ");
            int w = Integer.parseInt(size[0]);
            int h = Integer.parseInt(size[1]);
            graph = new int[h][w];
            fires.clear();
            person.clear();
            int answer = Integer.MAX_VALUE;
            int time = 0;
            for (int j = 0; j < h; j++) {
                char[] s = br.readLine().toCharArray();
                for (int k = 0; k < s.length; k++) {
                    if (s[k] == '#') {
                        graph[j][k] = WALL;
                    } else if (s[k] == '.') {
                        graph[j][k] = EMPTY_SPACE;
                    } else if (s[k] == '*') {
                        graph[j][k] = FIRE;
                        fires.add(new int[] {j,k,0});
                    } else {
                        graph[j][k] = PERSON;
                        person.add(new int[]{j,k,0});
                    }
                }
            }
            // 불 먼저 움직이고 사람 움직이기
            while (!person.isEmpty()) {
                while (fires.peek() != null && fires.peek()[2] == time ) {
                    int[] fire = fires.poll();
                    for (int j = 0; j < dx.length; j++) {
                        int cy = fire[0] + dy[j];
                        int cx = fire[1] + dx[j];

                        if (cx < 0 || cy < 0 || cx >= w || cy >= h) continue;
                        if (graph[cy][cx] == FIRE) continue;
                        if (graph[cy][cx] != WALL) {
                            graph[cy][cx] = FIRE;
                            fires.add(new int[]{cy, cx, fire[2] + 1});
                        }
                    }
                }

                while (person.peek() != null && person.peek()[2] == time) {
                    int[] p = person.poll();
                    for (int j = 0; j < dx.length; j++) {
                        int cy = p[0] + dy[j];
                        int cx = p[1] + dx[j];

                        if (cx < 0 || cy < 0 || cx >= w || cy >= h) {
                            answer = Math.min(answer,p[2]);
                            break;
                        }
                        if (graph[cy][cx] == EMPTY_SPACE) {
                            graph[cy][cx] = PERSON;
                            person.add(new int[]{cy, cx, p[2] + 1});
                        }
                    }
                }
                time++;
            }
            if (answer == Integer.MAX_VALUE)
                sb.append("IMPOSSIBLE").append("\n");
            else
                sb.append(answer+1).append("\n");
        }
        System.out.println(sb);
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

1 초 (추가 시간 없음) 1024 MB 11094 4316 2704 35.514%

문제

상어 중학교의 코딩 동아리에서 게임을 만들었다. 이 게임은 크기가 N×N인 격자에서 진행되고, 초기에 격자의 모든 칸에는 블록이 하나씩 들어있고, 블록은 검은색 블록, 무지개 블록, 일반 블록이 있다. 일반 블록은 M가지 색상이 있고, 색은 M이하의 자연수로 표현한다. 검은색 블록은 -1, 무지개 블록은 0으로 표현한다. (i, j)는 격자의 i번 행, j번 열을 의미하고, |r1 - r2| + |c1 - c2| = 1을 만족하는 두 칸 (r1, c1)과 (r2, c2)를 인접한 칸이라고 한다.

블록 그룹은 연결된 블록의 집합이다. 그룹에는 일반 블록이 적어도 하나 있어야 하며, 일반 블록의 색은 모두 같아야 한다. 검은색 블록은 포함되면 안 되고, 무지개 블록은 얼마나 들어있든 상관없다. 그룹에 속한 블록의 개수는 2보다 크거나 같아야 하며, 임의의 한 블록에서 그룹에 속한 인접한 칸으로 이동해서 그룹에 속한 다른 모든 칸으로 이동할 수 있어야 한다. 블록 그룹의 기준 블록은 무지개 블록이 아닌 블록 중에서 행의 번호가 가장 작은 블록, 그러한 블록이 여러개면 열의 번호가 가장 작은 블록이다.

오늘은 이 게임에 오토 플레이 기능을 만드려고 한다. 오토 플레이는 다음과 같은 과정이 블록 그룹이 존재하는 동안 계속해서 반복되어야 한다.

  1. 크기가 가장 큰 블록 그룹을 찾는다. 그러한 블록 그룹이 여러 개라면 포함된 무지개 블록의 수가 가장 많은 블록 그룹, 그러한 블록도 여러개라면 기준 블록의 행이 가장 큰 것을, 그 것도 여러개이면 열이 가장 큰 것을 찾는다.
  2. 1에서 찾은 블록 그룹의 모든 블록을 제거한다. 블록 그룹에 포함된 블록의 수를 B라고 했을 때, B점을 획득한다.
  3. 2
  4. 격자에 중력이 작용한다.
  5. 격자가 90도 반시계 방향으로 회전한다.
  6. 다시 격자에 중력이 작용한다.

격자에 중력이 작용하면 검은색 블록을 제외한 모든 블록이 행의 번호가 큰 칸으로 이동한다. 이동은 다른 블록이나 격자의 경계를 만나기 전까지 계속 된다.

다음은 N = 5, M = 3인 경우의 예시이다.

2 2 -1 3 1

3 3 2 0 -1
0 0 0 1 2
-1 3 1 3 2
0 3 2 2 1

여기서 찾을 수 있는 크기가 가장 큰 블록 그룹을 다음과 같이 빨간색으로 표시했다.

2 2 -1 3 1

3 3 2 0 -1
0 0 0 1 2
-1 3 1 3 2
0 3 2 2 1

블록 그룹이 제거되면 다음과 같이 변하고, 점수 82점을 획득한다.

2 2 -1 3 1

    2 0 -1
      1 2
-1   1 3 2
    2 2 1

중력이 작용하면 다음과 같이 변한다.

-1 3 1

      0 -1
2   2 1 2
-1   1 3 2
  2 2 2 1

90도 반시계방향으로 회전한 결과는 다음과 같다.

1 -1 2 2 1

3 0 1 3 2
-1   2 1 2
        2
    2 -1  

다시 여기서 중력이 작용하면 다음과 같이 변한다.

1 -1

3   2 2 1
-1   1 3 2
    2 1 2
  0 2 -1 2

오토 플레이가 모두 끝났을 때 획득한 점수의 합을 구해보자.

입력

첫째 줄에 격자 한 변의 크기 N, 색상의 개수 M이 주어진다.

둘째 줄부터 N개의 줄에 격자의 칸에 들어있는 블록의 정보가 1번 행부터 N번 행까지 순서대로 주어진다. 각 행에 대한 정보는 1열부터 N열까지 순서대로 주어진다. 입력으로 주어지는 칸의 정보는 -1, 0, M이하의 자연수로만 이루어져 있다.

출력

첫째 줄에 획득한 점수의 합을 출력한다.

제한

  • 1 ≤ N ≤ 20
  • 1 ≤ M ≤ 5

풀이

소요 시간

100분

문제에 제시된 오토 플레이 기능을 그대로 구현하면 된다.

  1. BFS를 통해 가장 큰 블록 그룹을 구한다.
  2. 블록 그룹을 제거한다. (EMPTY_BLOCK 으로 마킹했다.)
  3. 중력을 적용한다
  4. 90도 반시계 방향으로 회전시킨다.
  5. 중력을 적용한다.
  6. 1~5를 반복한다. 만약 1번 수행 후 가장 큰 블록 그룹의 블록 갯수가 2개 미만이라면 종료한다.

헤맸던 부분

블록 그룹의 기준 블록은 무지개 블록이 아닌 블록 중에서 행의 번호가 가장 작은 블록, 그러한 블록이 여러개면 열의 번호가 가장 작은 블록이다.

이거 잘못 읽어서 행 길이, 열 길이로 구했다가 한 20분 날려먹었다 문제 잘읽자..

코드

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class B21609 {
    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,-1,1};

    static int N;
    static int M;
    static int[][] arr;
    static boolean[][] visited;
    static int answer = 0;

    static int cnt = 0; // 블록 갯수
    static int rainbowCnt = 0; // 무지개 블록 갯수
    static int targetI = 0; // 행 번호
    static int targetJ = 0; // 열 번호
    static int target = 0;
    static int EMPTY_BLOCK = -2;
    static ArrayList<int[]> targetList = new ArrayList<>();
    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());

        arr = new int[N][N];
        visited = new boolean[N][N];

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        while (true) {
            visited = new boolean[N][N];
            cnt = 0; // 블록 갯수
            rainbowCnt = 0; // 무지개 블록 갯수
            targetI = 0; // 행 갯수
            targetJ = 0; // 열 갯수
            target = 0;
            targetList.clear();
            // BFS로 가장 큰 블록 탐색
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (!visited[i][j] && arr[i][j] != -1 && arr[i][j] != 0 && arr[i][j] != EMPTY_BLOCK) {
                        visited[i][j] = true;
                        bfs(i,j);

                    }
                }
            }

            if (cnt < 2) break;

            answer += cnt*cnt;

            // 가장 큰 블록 없애기
            setBlockEmpty();

            // 블록에 중력 적용하기
            gravity();

            // 90도 반시계 방향 회전
            rotation();

            // 블록에 중력 적용하기
            gravity();
        }
        System.out.println(answer);
    }
    public static void bfs(int i, int j) {
        int cCnt = 1; // 블록 갯수
        int cRainbowCnt = 0; // 무지개 블록 갯수
        int cTargetI = i; // 행 번호
        int cTargetJ = j; // 열 번호
        int cTarget = arr[i][j];
        ArrayList<int[]> cTargetList = new ArrayList<>();
        cTargetList.add(new int[] {i,j});
        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[] {i,j});
        while (!queue.isEmpty()) {
            int[] value = queue.poll();

            for (int k = 0; k < dx.length; k++) {
                int cx = value[0] + dx[k];
                int cy = value[1] + dy[k];

                if (cx < 0 || cy < 0 || cx >= N || cy >= N) continue;
                if (visited[cx][cy]) continue;
                if (arr[i][j] == arr[cx][cy]) {
                    cCnt++;
                    queue.add(new int[] {cx, cy});
                    cTargetList.add(new int[] {cx,cy});
                    visited[cx][cy] = true;
                } else if (arr[cx][cy] == 0) {
                    cCnt++;
                    cRainbowCnt++;
                    queue.add(new int[] {cx, cy});
                    cTargetList.add(new int[] {cx,cy});
                    visited[cx][cy] = true;
                }
            }
        }
        if (cTargetList.size() < 2) return;
        selectTarget(cCnt, cRainbowCnt, cTargetI, cTargetJ, cTarget, cTargetList);

        for (int k = 0; k < cTargetList.size(); k++) {
            // 타겟이 선정되고 난 후 값이 0인 위치는 공유 가능하므로 다시 visited를 false로 변경해준다.
            int[] t = cTargetList.get(k);
            if (arr[t[0]][t[1]] == 0)
                visited[t[0]][t[1]] = false;
        }

    }
    public static void selectTarget(int cCnt, int cRainbowCnt, int cTargetI, int cTargetJ, int cTarget, ArrayList<int[]> cTargetList) {
        if (cCnt> cnt) {
            cnt = cCnt;
            rainbowCnt = cRainbowCnt;
            targetI = cTargetI;
            targetJ = cTargetJ;
            target = cTarget;
            targetList = cTargetList;
        } else if (cCnt == cnt) {
            if (cRainbowCnt > rainbowCnt) {
                cnt = cCnt;
                rainbowCnt = cRainbowCnt;
                targetI = cTargetI;
                targetJ = cTargetJ;
                target = cTarget;
                targetList = cTargetList;
            } else if (cRainbowCnt == rainbowCnt) {
                if (cTargetI > targetI) {
                    cnt = cCnt;
                    rainbowCnt = cRainbowCnt;
                    targetI = cTargetI;
                    targetJ = cTargetJ;
                    target = cTarget;
                    targetList = cTargetList;
                } else if (cTargetI == targetI) {
                    if (cTargetJ > targetJ) {
                        cnt = cCnt;
                        rainbowCnt = cRainbowCnt;
                        targetI = cTargetI;
                        targetJ = cTargetJ;
                        target = cTarget;
                        targetList = cTargetList;
                    }
                }
            }
        }
    }
    public static void setBlockEmpty() {
        for (int i = 0; i < targetList.size(); i++) {
            int[] target = targetList.get(i);
            arr[target[0]][target[1]] = EMPTY_BLOCK;
        }
    }
    public static void gravity() {
        for(int i = 0; i < N; ++i)
        {
            for(int j = N-1; j >= 0; --j)
            {
                if(arr[j][i] == EMPTY_BLOCK || arr[j][i] == -1) continue;
                int idx = j+1;
                while(true)
                {
                    if(idx == N) break;
                    if(arr[idx][i] == EMPTY_BLOCK) idx++;
                    else break;
                }
                if(idx == j+1) continue;
                arr[idx-1][i] = arr[j][i];
                arr[j][i] = EMPTY_BLOCK;
            }
        }
    }
    public static void rotation() {
        int [][] tmp = new int[N][N];
        for(int i = 0; i < N; ++i)
        {
            for(int j = 0; j < N; ++j)
            {
                tmp[N-j-1][i] = arr[i][j];
            }
        }
        for(int i = 0; i < N; ++i)
        {
            System.arraycopy(tmp[i],0,arr[i],0,N);
        }
    }
}

개요

스프링 부트 프로젝트 생성 후 main 실행 시 아래의 로그와 함께 실행에 실패했다.

아래의 로그에서 알 수 있듯이, 8080 포트가 이미 사용중이므로 실행에 실패한것을 알 수 있다.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.7.16)

2023-10-03 22:35:24.447  INFO 24480 --- [           main] c.h.springboot.SpringbootApplication     : Starting SpringbootApplication using Java 11.0.17 on DESKTOP-5TJ7OBF with PID 24480 (D:\GitRepository_HW\springboot-2023\out\production\classes started by HyeonWoo in D:\GitRepository_HW\springboot-2023)
2023-10-03 22:35:24.451  INFO 24480 --- [           main] c.h.springboot.SpringbootApplication     : No active profile set, falling back to 1 default profile: "default"
2023-10-03 22:35:25.689  INFO 24480 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-10-03 22:35:25.701  INFO 24480 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-10-03 22:35:25.702  INFO 24480 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.80]
2023-10-03 22:35:25.836  INFO 24480 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-10-03 22:35:25.836  INFO 24480 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1303 ms
2023-10-03 22:35:26.071  INFO 24480 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: class path resource [static/index.html]
2023-10-03 22:35:26.178  WARN 24480 --- [           main] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates, check your Thymeleaf configuration, or set spring.thymeleaf.check-template-location=false)
2023-10-03 22:35:26.220  WARN 24480 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.boot.web.server.PortInUseException: Port 8080 is already in use
2023-10-03 22:35:26.223  INFO 24480 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2023-10-03 22:35:26.235  INFO 24480 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-10-03 22:35:26.251 ERROR 24480 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Web server failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.


Process finished with exit code 1

또한 localhost 접속 시 서버를 실행하지 않았음에도 아래와 같이 It works! 라는 문구를 볼 수 있었다.

 

 

원인파악

예상했던대로, 8080포트를 사용할 것으로 예상되는 프로그램이 실행중인것을 작업관리자에서 확인할 수 있었다.

이 프로그램 httpd는 아파치 하이퍼텍스트 전송 프로토콜 (HTTP) 서버 프로그램이다.

아파치 설치시에 서비스 또는 시작 프로그램에 등록된 것으로 보인다.

여러가지 방법을 통해 이를 해결할 수 있겠지만, 나는 두가지의 경우만 확인해봤다.

1. SpringBoot의 기본 서버 포트를 변경한다.

2. 아파치 httpd를 서비스 또는 시작 프로그램에서 제거하기

 

오류 해결

1. SpringBoot의 기본 서버 포트를 변경한다.

이 경우 application.properties 파일에 새로운 옵션을 추가한다. server.port = [원하는 포트] 를 입력하여 쉽게 해결할 수 있다.

 

 

2. 아파치 httpd를 서비스 또는 시작 프로그램에서 제거하기.

역시나 service 목록에 아파치가 등록되어 있는것을 확인했다. 

Windows + R 버튼을  눌러 실행 창을 띄운 후 services.msc를 입력하여 서비스 창을 띄운 후 Apache2.4를 중지하면 된다.

컴퓨터를 새로 킬 때마다 실행되는 것을 막으려면 오른쪽 마우스로 Apache2.4를 클릭 후 속성에서 시작유형을 사용 안 함으로 변경하자.

 

Reference

https://zetawiki.com/wiki/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8_%EC%84%9C%EB%B2%84_%ED%8F%AC%ED%8A%B8_%EB%B3%80%EA%B2%BD

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 128 MB 3585 1277 1001 37.323%

문제

한글 프로그램의 메뉴에는 총 N개의 옵션이 있다. 각 옵션들은 한 개 또는 여러 개의 단어로 옵션의 기능을 설명하여 놓았다. 그리고 우리는 위에서부터 차례대로 각 옵션에 단축키를 의미하는 대표 알파벳을 지정하기로 하였다. 단축키를 지정하는 법은 아래의 순서를 따른다.

  1. 먼저 하나의 옵션에 대해 왼쪽에서부터 오른쪽 순서로 단어의 첫 글자가 이미 단축키로 지정되었는지 살펴본다. 만약 단축키로 아직 지정이 안 되어있다면 그 알파벳을 단축키로 지정한다.
  2. 만약 모든 단어의 첫 글자가 이미 지정이 되어있다면 왼쪽에서부터 차례대로 알파벳을 보면서 단축키로 지정 안 된 것이 있다면 단축키로 지정한다.
  3. 어떠한 것도 단축키로 지정할 수 없다면 그냥 놔두며 대소문자를 구분치 않는다.
  4. 위의 규칙을 첫 번째 옵션부터 N번째 옵션까지 차례대로 적용한다.

입력

첫째 줄에 옵션의 개수 N(1 ≤ N ≤ 30)이 주어진다. 둘째 줄부터 N+1번째 줄까지 각 줄에 옵션을 나타내는 문자열이 입력되는데 하나의 옵션은 5개 이하의 단어로 표현되며, 각 단어 역시 10개 이하의 알파벳으로 표현된다. 단어는 공백 한 칸으로 구분되어져 있다.

출력

N개의 줄에 각 옵션을 출력하는데 단축키로 지정된 알파벳은 좌우에 [] 괄호를 씌워서 표현한다.

풀이

시간 복잡도

N = 30, 단어 갯수 = 5, 단어 길이 = 10 이므로, 시간제한 2초에는 충분히 들어올 수 있다.

  1. 문자열을 단어 단위로 분리한다.
  2. 단어의 첫번째 글자가 단축키가 될 수 있다면 해당 문자열 배열의 값을 변경 함.
  3. 만약 2번 과정에서 단축키를 찾지 못했다면 문자열 순서대로 단축키로 사용가능한 값을 찾아서 해당 문자열 배열의 값을 변경 함.
  4. 분리한 문자열을 합쳐서 출력

코드

package com.company.baekjoon;

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

public class B1283 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        int N = Integer.parseInt(br.readLine());
        HashSet<Character> set = new HashSet<>();

        StringBuilder result = new StringBuilder();
        for (int i = 0; i < N; i++) {
            boolean hasCommand = false;
            String[] input = br.readLine().split(" ");

            for (int j = 0; j < input.length; j++) {
                if (!set.contains(Character.toLowerCase(input[j].charAt(0))) && !hasCommand) {
                    set.add(Character.toLowerCase(input[j].charAt(0)));
                    result.append(input[j]);
                    result.insert(0, "[");
                    result.insert(2, "]");
                    input[j] = result.toString();
                    hasCommand = true;
                    result.setLength(0);

                }
            }
            if (!hasCommand) {
                for (int j = 0; j < input.length; j++) {
                    for (int k = 0; k < input[j].length(); k++) {
                        if (!set.contains(Character.toLowerCase(input[j].charAt(k))) && !hasCommand) {
                            set.add(Character.toLowerCase(input[j].charAt(k)));
                            result.append(input[j]);
                            result.insert(k, "[");
                            result.insert(k+2, "]");
                            input[j] = result.toString();
                            hasCommand = true;
                            result.setLength(0);
                        }
                    }
                }
            }
            for (int j = 0; j < input.length; j++) {
                if (j == input.length -1) sb.append(input[j]);
                else sb.append(input[j]).append(" ");
            }
            sb.append("\\n");
        }
        System.out.println(sb);
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

1 초 512 MB 15785 9277 6176 57.095%

문제

길이가 N인 컨베이어 벨트가 있고, 길이가 2N인 벨트가 이 컨베이어 벨트를 위아래로 감싸며 돌고 있다. 벨트는 길이 1 간격으로 2N개의 칸으로 나뉘어져 있으며, 각 칸에는 아래 그림과 같이 1부터 2N까지의 번호가 매겨져 있다.

벨트가 한 칸 회전하면 1번부터 2N-1번까지의 칸은 다음 번호의 칸이 있는 위치로 이동하고, 2N번 칸은 1번 칸의 위치로 이동한다. i번 칸의 내구도는 Ai이다. 위의 그림에서 1번 칸이 있는 위치를 "올리는 위치", N번 칸이 있는 위치를 "내리는 위치"라고 한다.

컨베이어 벨트에 박스 모양 로봇을 하나씩 올리려고 한다. 로봇은 올리는 위치에만 올릴 수 있다. 언제든지 로봇이 내리는 위치에 도달하면 그 즉시 내린다. 로봇은 컨베이어 벨트 위에서 스스로 이동할 수 있다. 로봇을 올리는 위치에 올리거나 로봇이 어떤 칸으로 이동하면 그 칸의 내구도는 즉시 1만큼 감소한다.

컨베이어 벨트를 이용해 로봇들을 건너편으로 옮기려고 한다. 로봇을 옮기는 과정에서는 아래와 같은 일이 순서대로 일어난다.

  1. 벨트가 각 칸 위에 있는 로봇과 함께 한 칸 회전한다.
  2. 가장 먼저 벨트에 올라간 로봇부터, 벨트가 회전하는 방향으로 한 칸 이동할 수 있다면 이동한다. 만약 이동할 수 없다면 가만히 있는다.
  3. 로봇이 이동하기 위해서는 이동하려는 칸에 로봇이 없으며, 그 칸의 내구도가 1 이상 남아 있어야 한다.
  4. 올리는 위치에 있는 칸의 내구도가 0이 아니면 올리는 위치에 로봇을 올린다.
  5. 내구도가 0인 칸의 개수가 K개 이상이라면 과정을 종료한다. 그렇지 않다면 1번으로 돌아간다.

종료되었을 때 몇 번째 단계가 진행 중이었는지 구해보자. 가장 처음 수행되는 단계는 1번째 단계이다.

입력

첫째 줄에 N, K가 주어진다. 둘째 줄에는 A1, A2, ..., A2N이 주어진다.

출력

몇 번째 단계가 진행 중일때 종료되었는지 출력한다.

제한

  • 2 ≤ N ≤ 100
  • 1 ≤ K ≤ 2N
  • 1 ≤ A ≤ 1,000
  • i

풀이

풀이시간 : 140분

문제를 이해하는데 어려웠던 부분

  • “올린다”와 “내린다”의 개념 파악 ⇒ N개 이상 부터(아래 컨베이어 벨트)는 로봇을 올리지 않아도 된다.
  • 벨트가 이동하는 것과 로봇이 이동하는 것은 별개 ⇒ 벨트는 매 루프마다 이동하고, 로봇은 따로 이동가능한지 여부를 따져서 이동
  • 가장 먼저 올라간 로봇 부터 이동 시작 ⇒ N번부터 0번 인덱스까지 로봇 이동가능 여부 판단 해야함 

사용 자료구조

  1. top Conveyor : 큐를 사용하면 좋겠지만, 로봇 이동을 위해 인덱스 접근이 필요하므로 ArrayList 사용
  2. bottom Conveyor : Deque를 사용하여 컨테이너 이동 구현

코드

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

public class Main {
    static int N;
    static int K;

    public static class Belt {
        int durability;
        boolean hasRobot;
        public Belt(int durability) {
            this.durability = durability;
        }

        @Override
        public String toString() {
            return durability+"";
        }
    }
    static ArrayList<Belt> topConvayor = new ArrayList<>();
    static ArrayDeque<Belt> bottomConvayor = new ArrayDeque<>();

    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());
        K = Integer.parseInt(st.nextToken());
        int loopCount = 0;
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            topConvayor.add(new Belt(Integer.parseInt(st.nextToken())));
        }
        for (int i = 0; i < N; i++) {
            bottomConvayor.add(new Belt(Integer.parseInt(st.nextToken())));
        }
        
        while (K > 0) {
            loopCount++;
            moveBelt();
            lowerRobot();
            moveRobot();
            lowerRobot();
            raiseRobot();

        }
        System.out.println(loopCount);
    }
    public static void moveBelt() {
        topConvayor.add(0, bottomConvayor.pollLast());
        bottomConvayor.addFirst(topConvayor.get(N));
        topConvayor.remove(N);
    }
    public static void moveRobot() {
        for (int i = N-1; i > 0; i--) {
            Belt currentBelt = topConvayor.get(i-1);
            Belt nextBelt = topConvayor.get(i);
            if (currentBelt.hasRobot && !nextBelt.hasRobot && nextBelt.durability >= 1) {
                // 현재 벨트위에 로봇이 있고, 다음 벨트에 로봇이 없으며 내구도가 1이상이라면 로봇을 이동시킬 수 있다.
                currentBelt.hasRobot = false;
                nextBelt.hasRobot = true;
                nextBelt.durability -= 1;
                if (nextBelt.durability == 0) {
                    K--;
                }
            }

        }
    }
    public static void raiseRobot() {
        Belt b = topConvayor.get(0);
        if (!b.hasRobot && b.durability >= 1) { // 0번째 벨트에 로봇이 없고, 내구도가 1이상이라면 로봇을 올릴 수 있다.
            topConvayor.get(0).hasRobot = true;
            topConvayor.get(0).durability -= 1;
            if (topConvayor.get(0).durability == 0) {
                K--;
            }
        }
    }
    public static void lowerRobot() {
        topConvayor.get(N-1).hasRobot = false;
    }
}

+ Recent posts