문제

NxN 크기의 시험관이 있다. 시험관은 1x1 크기의 칸으로 나누어지며, 특정한 위치에는 바이러스가 존재할 수 있다. 모든 바이러스는 1번부터 K번까지의 바이러스 종류 중 하나에 속한다.

시험관에 존재하는 모든 바이러스는 1초마다 상, 하, 좌, 우의 방향으로 증식해 나간다. 단, 매 초마다 번호가 낮은 종류의 바이러스부터 먼저 증식한다. 또한 증식 과정에서 특정한 칸에 이미 어떠한 바이러스가 존재한다면, 그 곳에는 다른 바이러스가 들어갈 수 없다.

시험관의 크기와 바이러스의 위치 정보가 주어졌을 때, S초가 지난 후에 (X,Y)에 존재하는 바이러스의 종류를 출력하는 프로그램을 작성하시오. 만약 S초가 지난 후에 해당 위치에 바이러스가 존재하지 않는다면, 0을 출력한다. 이 때 X와 Y는 각각 행과 열의 위치를 의미하며, 시험관의 가장 왼쪽 위에 해당하는 곳은 (1,1)에 해당한다.

예를 들어 다음과 같이 3x3 크기의 시험관이 있다고 하자. 서로 다른 1번, 2번, 3번 바이러스가 각각 (1,1), (1,3), (3,1)에 위치해 있다. 이 때 2초가 지난 뒤에 (3,2)에 존재하는 바이러스의 종류를 계산해보자.

1초가 지난 후에 시험관의 상태는 다음과 같다.

2초가 지난 후에 시험관의 상태는 다음과 같다.

결과적으로 2초가 지난 뒤에 (3,2)에 존재하는 바이러스의 종류는 3번 바이러스다. 따라서 3을 출력하면 정답이다.

 

푸는 TIP

큐 배열을 만들어서, 해당 바이러스가 번식하면 큐에 새로운 인자를 넣어주고, 만약 새로운 인자가 들어갔다면, 다시 그 위치를 넣어주는 방식으로 바이러스를 늘려간다.

전형적인 BFS 문제이지만, 바이러스 별로 순서에 맞춰서 늘려간다는 것이 조금 새로운 방법이었던 것 같다.

소스 코드

#include <iostream>
#include <queue>
#define MAX 201
using namespace std;
int N, K, S, X, Y;
int mat[MAX][MAX];
int dx[] = {0, 0, -1, 1};
int dy[] = {-1, 1, 0, 0};

bool notBound(int x, int y) {
    if(x >= 1 && x <= N && y >= 1 && y <= N) return true;
    else return false;
}

void BFS(queue<pair<int, int>> *Q) {
    for(int i=1; i<=K; i++) {
        int size = Q[i].size();
        for(int j=0; j<size; j++) {
            bool isInside = false;
            int x = Q[i].front().first;
            int y = Q[i].front().second;
            Q[i].pop();
            for(int k=0; k<4; k++) {
                int nx = x+dx[k];
                int ny = y+dy[k];
                if(notBound(nx, ny) && mat[nx][ny] == 0) {
                    isInside = true;
                    Q[i].push({nx, ny});
                    mat[nx][ny] = mat[x][y];
                }
            }
            if(isInside) Q[i].push({x, y});
        }
    }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> N >> K;
    queue<pair<int, int>> Q[K+1];
    for(int i=1; i<=N; i++) {
        for(int j=1; j<=N; j++) {
            cin >> mat[i][j];
            if(mat[i][j] != 0) Q[mat[i][j]].push({i, j});
        }
    }
    
    cin >> S >> X >> Y;
    while(S--) {
        BFS(Q);
    }
    cout << mat[X][Y];
}

[링크]

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

'코딩 테스트 연습 > BaekJoon' 카테고리의 다른 글

[백준] 2798번 : 블랙잭  (0) 2020.07.29
[백준] 15649번 : N과 M(1) [미해결]  (0) 2020.07.29

이번 학기에 3학년이 되는 나는 뭐라도 해야 된다는 생각이 너무나 많았다.

그래서 코딩캠프 같은걸 찾아보니 SW마에스트로(이하 소마), 42서울, 우아한 테크코스 등이 유명했다.

그래서 지금 할 수 있는 것을 찾아보니 소마와 42서울이 지금 지원이 가능한 것들이라서 일단 신청서를 작성했다.


먼저 회원가입을 한 후에, 테스트를 본다. 기억력 테스트와 논리력 테스트, 2개를 테스트하는데 나는 기억력 13단계, 논리력 10단계를 통과했던 것 같다. 논리력 테스트를 보는데 9단계에서 애먹다가 겨우 통과했다. 운이 좋았던 것 같다.

다른 블로그들을 보면 커트라인에 겨우 걸쳤던 것 같다.

운이 좋게 통과한 듯!


다음으로 체크인 미팅을 신청해야한다. 마치 수강신청처럼 체크인 미팅이 열리는 날짜에 4시 42분에 열리는 미팅을 잡아야한다. 다른 블로그를 참고해서 창을 여러개 띄워놓고, 새로고침을 해보면서 뜨자마자 바로 누르니 연결되었다! 평소에 전공 여석이 없었던 수강신청과 비교하면 훨씬 널럴했던 것 같다. 지금은 코로나19 상황을 고려해서 모두 온라인으로 진행하는 듯 했다. 오전 10시부터 오후 4시까지 본인 인증을 완료하면, 체크인미팅 Q&A 줌 코드를 줘서 오후 4시부터 Q&A 시간을 갖는다.

나 같은 경우에는 10시에 바로 하면 사람들이 몰릴 것같아서 약 11시 30분정도에 들어가니까 대기없이 바로 입장이 됬고, 신분증 인증하니 완료됬다!


오후 4시가 되고, Q&A시간에 진짜 많은 사람들이 질문을 올려주었다. 원래는 4시 30분까지만 질문을 받으려 했는데, 질문들이 너무 많아서 중복되는 질문이나 홈페이지 FAQ를 참고하라고 하고 빠르게 질의응답을 이어갔다.

Q&A 시간이 끝나고는, 개포에 있는 건물 온라인 투어가 진행되었다. 화면으로만 봐도 맥북이 엄청나게 많았다. 


42서울 라피신 일정이다. 1달간 진행된다.

 

설명을 들어보니까 라피신이 끝나면 바로 본과정으로 들어가는데, 시간적으로는 굉장히 안좋은 것 같다. 일단 7기 1차 4월 11일~5월 6일으로 하면 학교 중간고사를 포기해야하고, 7기 2차를 신청하면 기말고사를 포기해야하는 시점... 사실 완전히 포기를 하는건 아니지만 거의 포기상태로 되는건 분명하다. 모든 사람들이 얘기하는게 학업과 병행하는게 너무나 시간적으로 힘들다는... 그렇다고 방학중에 하려고 8기 1차에 지원하면 본과정이 11월로 밀리니까 2학기에 계획이 좀 꼬이게 되는 측면이... 선택하기 어렵지만 라피신에 들어갈 수 있는 기회를 얻었으니 잘해봐야겠다.(100만원 지원금은 덤!)


사실 42서울과 소마를 둘 다 지원한 입장으로 둘 중에 하나라도 되면 좋겠다... 솔직한 심정으로는 서울 4년제 대학 컴퓨터학부 3학년으로 재학중인데, 42서울에서 많이 얻어갈 수 있을지는 잘 모르겠다. 만약에 본과정에 들어간다고 해도 그냥 학부 공부로부터 도망치는 게 아닌지... 이런 고민을 갖고 일단은 라피신 신청일을 기다려봐야겠다.

ps. SW마에스트로 잘 되게 해주세요!

vector란?

C++에서 동적 배열을 빠르게 사용할 수 있는 배열이라고 생각하면 쉽다! 배열에 새로운 원소를 추가하거나 삭제하고, 내부의 함수를 이용해서 크기와 같은 데이터를 출력하기 쉽다.

사용법 (예제)

#include <iostream>
#include <vector>
using namespace std;

int main() {
	vector <int> Vec;
	Vec.push_back(1);
	Vec.push_back(2);
	cout << Vec[0] << " " << Vec[1];
    return 0;
}

예제를 실행시켰을 때, 출력되는 값

헤더파일

#include <vector>

사용 방법

#include <vector>
using namespace std;
int main() {
	vector <int> V;
}

vector 생성 방법

#include <vector>
using namespace std;
int main() {
	vector <int> V1;	//크기 지정하지 않고 바로 정의
	vector <int> V2 (4);	//크기를 4로 지정하면서 정의
	vector <int> V3 (4, 0); //크기를 4로 지정하고, 각 원소의 값을 0으로 초기화
	vector <int> V4 (V3);   //V3의 벡터를 복사하여 정의
}

vector 내부에 데이터 저장하는 법

#include <vector>
#include <iostream>
using namespace std;
int main() {
	int N=4;
	vector <int> V1;
	vector <int> V2 (N);
	
	for(int i=0; i<N; i++) {
		V1.push_back(i);
	}
	
	for(int i=0; i<N; i++) {
		V2[i] = i;
	}
	return 0;
}

크기가 지정되지 않은 경우 push_back함수를 이용해서 배열의 마지막에 새로운 원소를 추가할 수 있다.

vector 내부의 함수들

V.size() 벡터의 크기를 반환
V.begin()
V.end()
벡터의 시작, 끝의 주소를 반환 - alogithm헤더의 sort 함수를 사용할 때 용이
V.front()
V.back()
벡터의 처음값, 끝값 반환
V.empty() 벡터가 비어있는지 확인
V1.swap(V2) 벡터 V1과 벡터 V2가 갖는 데이터를 서로 바꿈
V.erase(V.begin+N) 벡터 V의 N번째 원소 삭제 - 괄호 안에 들어있는 값은 주소로 
V.insert(V.begin+N, K) 벡터 V의 N번째 index에 K라는 데이터 추가
V.push_back(K)
V.pop_back()
벡터 V의 마지막 원소 추가/제거
V.resize(N) 벡터 V의 크기를 N으로 재설정
V.clear() 벡터 V의 모든 데이터 삭제
vector <vector <int>> V 2차원 벡터 생성

끝 마치며

벡터를 사용하면 배열을 사용하는 것보다 편리하게 원소를 추가하거나 삭제하고, 크기를 확인하기 쉽다. 특히 다른 함수로 배열을 넘기는 경우 배열의 크기를 찾기 굉장히 힘든데, 이때 벡터를 이용하면 V.size()함수를 이용해서 빠르게 구한다.

#include <iostream>
using namespace std;
int Answer;

int main(int argc, char** argv)
{
	int T, test_case, N;

	cin >> T;
	for(test_case = 0; test_case  < T; test_case++)
	{
		Answer = 0;
		cin >> N;
		for(int i=2; i<N; i++) {
			if(N / i == N % i) {
				Answer = i;
				break;
			}
		}
				
		cout << "Case #" << test_case+1 << endl;
		cout << Answer << endl;
	}

	return 0;
}

문제

자연수를 표시하기 위한 표기법으로는 10을 기저로 하는 10진법을 가장 많이 사용한다.
하지만, 그 이외에도 어떤 자연수이든지 기저로 하여 표현하는 것이 가능하다.
예를 들어 10을 기저로 하는 10진법에서 238로 표현되는 수가, 2를 기저로 하는 2진법에서는 11101110으로 표현된다.
그리고, 같은 수를 23진법으로 표현하면 10, 8로 표현될 것이다.
(23진법에서는 각 자리수는 10진수로 표현하고 자리수 사이를 콤마로 구분하였다.
 기저가 10 이상인 경우에만 이렇게 콤마로 구분하는 방법을 쓴다.) 

자연수 NN 이 기저 bb 에 대한 bb 진법으로 표현했을 때 모든 자리수의 값이 같게 되는 경우 NN  bb 에 대해서 “균일수”라고 부른다.
예를 들어, 10진수 36은 기저 17에 대해서 2,2로 표현되므로, 10진수 36은 17에 대해서 균일수가 되고,
10진수 36은 또한 기저 8에 대해서도 균일수가 된다. 그 이유는 10진수 36의 8진수 표현이 44이기 때문이다.
자연수 NN 이 하나 이상의 기저에 대해 균일수가 된다면, 그 기저들 중 가장 작은 값을 구하려고 한다.
극단적인 경우, 만약 b>Nb>N 이라면 NN  bb 진법으로 한자리 수가 되는데, 이 때도 균일수가 되는 것으로 간주한다.
자연수  NN 이 주어졌을 때, NN  bb 에 대해서 균일수가 되는 최소의 양의 정수 bb 를 구하는 프로그램을 작성하시오.
단, b 는 2 이상이어야 한다.


- 제한시간: 전체 테스트 케이스는 100개 이하이며, 전체 수행 시간은 1초 이내 (Java 2초 이내) 
    제한 시간을 초과하면 제출한 소스코드의 프로그램이 즉시 종료되며,
    그때까지 수행한 결과에서 테스트 케이스를 1개 그룹 이상 통과하였더라도 점수는 0점이 됩니다.
    그러나, 제한 시간을 초과하더라도 테스트 케이스를 1개 그룹 이상 통과하였다면 '부분 점수(0< 점수< 만점)'를 받을 수 있으며,
    이를 위해서는, C / C++ 에서 "printf 함수" 사용할 경우, 프로그램 시작부분에서 "setbuf(stdout, NULL);"를 한번만 사용하십시오.
    C++에서는 "setbuf(stdout, NULL);"와 "printf 함수" 대신 "cout"를 사용하고, Java에서는 "System.out.printIn"을 사용하시면,
    제한 시간을 초과하더라도 '부분 점수'를 받을 수 있습니다.                                     ※ 언어별 기본 제공 소스코드 내용 참고
    만약, 제한 시간을 초과하지 않았는데도 '부분 점수'를 받았다면, 일부 테스트 케이스를 통과하지 못한 경우 입니다.

- 메모리 사용 제한 : heap, global, static 총계 256MB, stack 1MB
- 제출 제한 : 최대 10회 (제출 횟수를 반영하여 순위 결정 → 동점자의 경우 제출 횟수가 적은 사람에게 높은 순위 부여)
- 점수 : 최대 10회 제출하여 취득한 각각의 점수 중에서 최대 점수

메모리 사용 제한

heap, global, static (총계) : 256MB
stack : 1MB

입력

입력 파일에는 여러 테스트 케이스가 포함될 수 있다.
파일의 첫 번째 줄에 테스트 케이스 개수를 나타내는 자연수 TT 가 주어지고,
이후 차례로 TT개의 테스트 케이스가 주어진다. ( 1T1001≤T≤100 )
두 번째 줄부터 주어지는 각각의 테스트 케이스는 자연수 NN 으로 주어진다. ( 1N1091≤N≤109 )

출력

각 테스트 케이스의 답을 순서대로 표준출력으로 출력하여야 하며, 각 테스트 케이스마다 첫 줄에 “Case #T”를 출력하여야 한다.
이때 TT는 테스트 케이스의 번호이다.
그 다음 줄에는 NN  bb 에 대해서 균일수가 되는 최소 양수 bb 를 출력한다. 단, bb 는 2이상이다.

느낀 점

i를 계속 증가시키면서 해당수가 균일수에 해당되는지 확인하면 되는 문제라 쉬운편에 속했다.

출처

[SCPC 예선]

문제


N 행 N 열로 이루어진 정사각형 격자구조 내에 방들 중에서
몇 개의 방에는 ╱ 또는 ╲ 형태의 대각선으로 45도 기울어진 양면거울이 들어 있다.
거울은 상하좌우에서 오는 빛을 다시 상하좌우 방향의 방으로 직각 반사시킨다.
본 문제에서는 '가장 윗줄에서 가장 왼쪽 방'의 왼쪽에서 레이저 포인터로 빛을 수평(0도)으로 비추었을 때
빛이 거치는 서로 다른 거울 개수를 계산하려고 한다.
 

 


예를 들어, 위 그림을 보자.
그림에서는 "①" 방으로 들어온 빛이 6번의 반사를 거친 후 3 ⅹ 3 크기의 격자구조 밖으로 빠져나간다.
그 중 "②" 방에 있는 거울의 경우, 한 거울의 양쪽 면에서 두 번의 반사가 일어난 것이다.
따라서 위의 예에서 빛을 반사한 거울은 5개이다. 이 문제를 해결하는 프로그램을 작성하시오.


- 제한 시간 : 전체 테스트 케이스는 20개 이하이며, 전체 수행 시간은 1초 이내 (Java 2초 이내) 
    제한 시간을 초과하면 제출한 소스코드의 프로그램이 즉시 종료되며,
    그때까지 수행한 결과에서 테스트 케이스를 1개 그룹 이상 통과하였더라도 점수는 0점이 됩니다.
    그러나, 제한 시간을 초과하더라도 테스트 케이스를 1개 그룹 이상 통과하였다면 '부분 점수(0< 점수< 만점)'를 받을 수 있으며,
    이를 위해서는, C / C++ 에서 "printf 함수" 사용할 경우, 프로그램 시작부분에서 "setbuf(stdout, NULL);"를 한번만 사용하십시오.
    C++에서는 "setbuf(stdout, NULL);"와 "printf 함수" 대신 "cout"를 사용하고, Java에서는 "System.out.printIn"을 사용하시면,
    제한 시간을 초과하더라도 '부분 점수'를 받을 수 있습니다.                                     ※ 언어별 기본 제공 소스코드 내용 참고
    만약, 제한 시간을 초과하지 않았는데도 '부분 점수'를 받았다면, 일부 테스트 케이스를 통과하지 못한 경우 입니다.

- 메모리 사용 제한 : heap, global, static 총계 256MB, stack 1MB
- 제출 제한 : 최대 10회 (제출 횟수를 반영하여 순위 결정 → 동점자의 경우 제출 횟수가 적은 사람에게 높은 순위 부여)
- 점수 : 최대 7회 제출하여 취득한 각각의 점수 중에서 최대 점수

메모리 사용 제한

heap, global, static (총계) : 256MB
stack : 1MB

입력

입력 파일에는 여러 개의 테스트 케이스가 포함되어 있다.
파일의 첫째 줄에는 테스트 케이스 개수를 나타내는 자연수 T 가 주어지고,
이후 차례로 TT 개의 테스트 케이스가 주어진다. ( 1T201≤T≤20
각각의 테스트 케이스 첫 번째 줄에는 정사각형 격자구조의 '한 변의 방 개수'를 나타내는 정수 N 이 주어진다. ( 1N1,0001≤N≤1,000 
다음 줄부터는 NN 개의 줄이 주어지고, 각 줄마다 NN 개의 문자가 주어진다, 각 문자는 { ‘0’, ‘1’ , ‘2’ }에 속하는 문자 중 하나이다.
문자 ‘1’은 우측 상단에서 좌측 하단으로 45도 기울어진 양면거울이 그 방에 있다는 것을 나타내며,
문자 ‘2’는 좌측 상단에서 우측 하단으로 45도 기울어진 양면거울이 그 방에 있다는 것을 나타낸다.
그리고 문자 ‘0’은 거울이 없는 방임을 나타낸다. 각 줄에 들어 있는 문자들 사이에 빈칸(공백)은 없다.

출력

각 테스트 케이스의 답을 순서대로 표준출력으로 출력하여야 하며, 각 테스트 케이스마다 첫 줄에 “Case #T”를 출력하여야 한다.
이때 T는 테스트 케이스의 번호이다.
그 다음 줄에는 정사각형 격자구조 건물의 '맨 위 맨 왼쪽 방'의 왼쪽에서 수평(0도)으로 들어간 빛이
그 건물을 빠져나올 때까지 거치는 서로 다른 거울의 개수를 출력한다.

입출력예

입력
3
2
02
11
3
201
220
210
4
2121
0102
2101
2121
출력
Case #1
3
Case #2
5
Case #3
8

푸는 TIP

구조체 room에 거울의 데이터를 저장하는 mirror, 이미 지나쳐 갔는지 확인하는 bool형 been 변수를 만들어 놓는다.

소스코드

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>
#pragma warning(disable : 4996) //비주얼스튜디오 scanf 오류 무시
int Answer;
typedef struct room {
	int mirror; //거울 데이터 저장
	bool been; //이미 갔는지 확인
}Room;
typedef struct location {
	int x;
	int y;
	char dir; //진행하려는 방향
	int count;
}Loc;
void myFlush(void) {
	while (getchar() != '\n')
		getchar();
}

Room** build_room(Room** room, int N) {
	int i = 0, j = 0, temp = 0;
	room = (Room**)malloc(sizeof(Room) * N);
	for (i = 0; i < N; i++) {
		room[i] = (Room*)malloc(sizeof(Room) * N);
	}
	for (i = 0; i < N; i++) {
		myFlush();
		for (j = 0; j < N; j++) {
			scanf("%c", &temp);
			room[j][i].mirror = temp-48; //%c로 받았으니 숫자가 아닌 문자로 인식되서 '0'을 받음으로 아스키 코드만큼 빼기
			if (temp == 0) {
				room[j][i].been = 0;
			}
			else {
				room[j][i].been = 1;
			}
		}
	}
	return room;
}
void loc_reset(Loc *loc) {
	loc->x = 0;
	loc->y = 0;
	loc->dir = 'R';
	loc->count = 0;
}
void make_dir_0(Loc * loc) {
	switch (loc->dir) {
	case 'L':
		loc->x--;
		break;
	case 'R':
		loc->x++;
		break;
	case 'U':
		loc->y--;
		break;
	case 'D':
		loc->y++;
		break;
	}
}
void make_dir_1(Loc * loc) {
	switch (loc->dir) {
	case 'L':
		loc->y++;
		loc->dir = 'D';
		break;
	case 'R':
		loc->y--;
		loc->dir = 'U';
		break;
	case 'U':
		loc->x++;
		loc->dir = 'R';
		break;
	case 'D':
		loc->x--;
		loc->dir = 'L';
		break;
	}
}
void make_dir_2(Loc * loc) {
	switch (loc->dir) {
	case 'L':
		loc->y--;
		loc->dir = 'U';
		break;
	case 'R':
		loc->y++;
		loc->dir = 'D';
		break;
	case 'U':
		loc->x--;
		loc->dir = 'L';
		break;
	case 'D':
		loc->x++;
		loc->dir = 'R';
		break;
	}
}
void endless_move(Room** room, Loc * loc, int N) {
	int check_mirror;
	while ((loc->x < N) && (loc->x >= 0) && (loc->y < N) && (loc->y >= 0)) {
		check_mirror = room[loc->x][loc->y].mirror;
		if (check_mirror == 0) {
			make_dir_0(loc);
		}
		else if (check_mirror == 1) {
			if (room[loc->x][loc->y].been == true) {
				room[loc->x][loc->y].been = false;
				loc->count++;
			}
			make_dir_1(loc);
		}
		else { //room[x][y]의 mirror 상태가 2인 경우
			if (room[loc->x][loc->y].been == true) {
				room[loc->x][loc->y].been = false;
				loc->count++;
			}
			make_dir_2(loc);
		}
	}
}
void set_free(Room*** room, int N) {
	for (int i = 0; i < N; i++) {
		free(room[i]);
	}
	free(room);
}

int main(void)
{
	int T, test_case;
	setbuf(stdout, NULL);
	Room** room = NULL;
	Loc loc;
	int N = 0;
	scanf("%d", &T);
	for (test_case = 0; test_case < T; test_case++)
	{
		loc_reset(&loc);
		scanf("%d", &N);
		room = build_room(room, N);
		endless_move(room, &loc, N);
		Answer = loc.count;
		printf("Case #%d\n", test_case + 1);
		printf("%d\n", Answer);
		set_free(&room, N);
	}
	return 0;
}

느낀 점

일단 메모리 오버플로우가 발생해서 실패상태이긴 하다.

모든 test case를 통과하긴 했지만 어느 부분이 메모리 초과를 발생하는지는 모르겠다. set_free함수로 malloc으로 메모리 설정해놓은거를 free 해줬지만, 

출처

[SCPC 2015 - 1차 예선]

 

문제

일직선 상에 돌들이 놓여있고, 개구리가 처음에는 '좌표 0'에 위치한 돌 위에 앉아 있다.
'좌표 0'에는 돌이 항상 놓여 있고, 모든 돌들은 정수 좌표에 놓여 있다. (그림 1)

 


[ 그림 1 ]



개구리는 점프를 통해서 돌들 사이를 이동해서 마지막 돌까지 이동해야 한다.
이 때, 개구리가 한번의 점프로 이동 가능한 최대 거리 KK 가 주어진다.
개구리는 한번의 점프로 자신이 앉아 있던 돌에서  KK 이하의 거리에 위치한 돌들 중 하나의 돌로 이동 할 수 있다. 
여기서 문제는, '좌표 0'에 위치한 개구리가 마지막 돌까지 이동할 수 있다면,
마지막 돌까지 이동하기 위한 최소 점프 횟수를 계산하는 것이다. 

예를 들어서, 위의 "그림1"의 예에서 보면, 한번의 점프로 이동 가능한 최대 거리가 K=4K=4 로 주어질 때,
아래 "그림2"에서와 같이 5회의 점프로 마지막 돌로 이동할 수 있고, 이것이 최소의 점프 횟수이다. 

 


[ 그림 2 ]



또한 위의 예에서, K=2K=2 로 주어진다면 개구리는 마지막 돌로 이동할 수가 없다.
왜냐하면, 개구리가 '좌표 2'의 돌로 이동한 후 '좌표 5'이상의 돌로는 이동할 수 없기 때문이다. 



- 제한시간 : 전체 테스트 케이스는 5개 이하이며, 전체 수행 시간은 1초 이내 (Java 2초 이내) 
    제한 시간을 초과하면 제출한 소스코드의 프로그램이 즉시 종료되며,
    그때까지 수행한 결과에서 테스트 케이스를 1개 그룹 이상 통과하였더라도 점수는 0점이 됩니다.
    그러나, 제한 시간을 초과하더라도 테스트 케이스를 1개 그룹 이상 통과하였다면 '부분 점수(0< 점수< 만점)'를 받을 수 있으며,
    이를 위해서는, C / C++ 에서 "printf 함수" 사용할 경우, 프로그램 시작부분에서 "setbuf(stdout, NULL);"를 한번만 사용하십시오.
    C++에서는 "setbuf(stdout, NULL);"와 "printf 함수" 대신 "cout"를 사용하고, Java에서는 "System.out.printIn"을 사용하시면,
    제한 시간을 초과하더라도 '부분 점수'를 받을 수 있습니다.                                     ※ 언어별 기본 제공 소스코드 내용 참고
    만약, 제한 시간을 초과하지 않았는데도 '부분 점수'를 받았다면, 일부 테스트 케이스를 통과하지 못한 경우 입니다.

- 메모리 사용 제한 : heap, global, static 총계 256MB, stack 1MB
- 제출 제한 : 최대 10회 (제출 횟수를 반영하여 순위 결정 → 동점자의 경우 제출 횟수가 적은 사람에게 높은 순위 부여)
- 제출 제한 : 최대 7회

메모리 사용 제한

heap, global, static (총계) : 256MB
stack : 1MB

입력

입력 파일에는 여러 개의 테스트 케이스가 포함될 수 있다.
파일의 첫째 줄에는 테스트 케이스 개수를 나타내는 자연수 TT가 주어지고,
이후 차례로 TT개의 테스트 케이스가 주어진다. ( 1T51≤T≤5 
각각의 테스트 케이스 첫 번째 줄에는 '좌표 0'에 놓인 돌을 제외한 나머지 돌들의 개수 NN 이 주어진다. ( 1N1,000,0001≤N≤1,000,000 )
두 번째 줄에는 돌들이 놓인 좌표를 나타내는 NN 개의 정수 aiai들이 빈칸(공백)을 사이에 두고 주어진다. (1ai1091≤ai≤109 )
여기서, 주어진 좌표들은 증가하는 순서로 주어지고 모두 다르다.
세 번째 줄에는 개구리가 한 번의 점프로 이동 가능한 최대 거리 KK 가 주어진다. ( 1K1091≤K≤109 )

출력

각 테스트 케이스의 답을 순서대로 표준출력으로 출력하여야 하며, 각 테스트 케이스마다 첫 줄에 “Case #T”를 출력하여야 한다.
이때 TT는 테스트 케이스의 번호이다.
그 다음 줄에는 개구리가 마지막 돌로 이동할 수 있는 최소 점프 횟수를 출력한다.
만약, 개구리가 마지막 돌로 이동하는 것이 불가능한 경우에는 "-1"을 출력한다.

입출력예

입력
3
8
1 2 5 7 9 10 12 15
4
8
1 2 5 7 9 10 12 15
2
17
3 4 8 10 14 18 20 22 23 25 30 33 34 363 38 39 42
7
출력
Case #1
5
Case #2
-1
Case #3
8

푸는 TIP

나같은 경우는 배열 입력을 받았을 때, arr[0]에 배열의 크기를 저장하고, 데이터들을 arr에 저장해준다. -> 배열이 포인터로 데이터를 옮기기 때문에 데이터의 크기가 얼마나 되는지 알 수 없다.(sizeof로) 따라서 따로 arr[0]에 size를 저장해준다.

그리고 jump로 저장된 최대 점프로 해보고, 만약 배열에 동일한 데이터가 없는 경우 -1인 상태로 데이터 있는지 확인한다. 이를 반복해서 확인해보고 가장 멀리 점프하도록 유도한다. 기능을 분리해야함으로 새로운 함수를 작성해서 적용한다.

소스 코드

#include <stdio.h>
#include <malloc.h>
int Answer;
int move(int * arr, int jump);
int checking(int* arr, int input);
int* insert_data(int* arr);
int can_Across(int * arr, int jump);

int main(void)
{
	int T, test_case;
	int* arr = NULL;
	int jump = 0;
	setbuf(stdout, NULL);
	scanf("%d", &T);

	for (test_case = 0; test_case < T; test_case++)
	{
		Answer = 0;
		arr = insert_data(arr);
		scanf("%d", &jump);
		if (can_Across(arr, jump) == 0) {
			Answer = -1;
		}
		else {
			Answer = move(arr, jump);
		}
		printf("Case #%d\n", test_case + 1);
		printf("%d\n", Answer);
		free(arr);
	}
	return 0;
}

int move(int * arr, int jump) {
	int moved = 0;
	int count = 0;
	int size = arr[0];
	int toMove=0;
	while(moved < arr[size]) {
		toMove=jump+1; //밑에 while문을 do while로 고치기 싫어서 사용 
		while(toMove--){
			if(checking(arr, moved+toMove)){
				moved = moved+toMove;
				count++;
				break;
			}
			else{
				continue;
			}
		}
	}
	return count;
}

int checking(int* arr, int input) {
	int size = arr[0];
	int i=1;
	for(i=1;i<=size;i++){
		if(input == arr[i]){
			return 1;
		}
	}
	return 0;
}
int* insert_data(int* arr) { //arr의 index 0에는 data의 개수를 저장 
	int size;
	scanf("%d",&size);
	arr = (int *)malloc(sizeof(int) * (size+1));
	int i=1;
	for (i = 1; i <= size; i++) {
		scanf("%d", &arr[i]);
	}
	arr[0] = size;
	return arr;
}

int can_Across(int * arr, int jump) {
	int size = arr[0];
	int i = 1;
	for (i = 1; i < size; i++) { //data 개수-1까지 확인하면 종료 
		if (arr[i + 1] - arr[i] > jump){
			return 0;
		}
		else {
			continue;
		}
	}
	return 1;
}

느낀 점

test case들은 모두 통과했지만 time out이 뜬 걸로 봐서 시간 복잡도가 매우 높은걸로 예상된다. 아마 moving 함수에서 for문 2개에다가 checking함수의 for문까지 하나, 총 3개의 for문과 1개의 함수를 이동하는 것 때문에 시간 복잡도가 O(N^3)이상으로 커진 것 같다. 다른 방법을 찾아봐야겠다.

출처

[SCPC 2015 - 1차 예선]

[문제]

당신은 프로포즈를 위해 촛불을 삼각형으로 배치하고 있다. 촛불을 K단 크기로 배치하면, 1단에는 K개의 양초, 2단에는 K-1개의 양초, …, K단에는 1개의 양초를 배치해서 총 (K(K+1))/2개의 양초가 필요하다.

당신이 사용할 양초의 개수 N이 주어질 때, 이 양초를 모두 사용하면 몇 단 크기의 촛불 삼각형을 만들 수 있는지 구하여라.

 

[입력]

첫 번째 줄에 테스트 케이스의 수 TC가 주어진다. 이후 TC개의 테스트 케이스가 새 줄로 구분되어 주어진다. 각 테스트 케이스는 다음과 같이 구성되었다.

첫 번째 줄에 양초 개수가 주어진다. (1≤N≤1018)

 


[출력]


각 테스트 케이스마다 ‘#x’(x는 테스트케이스 번호를 의미하며 1부터 시작한다)를 출력하고,
각 테스트 케이스 마다 주어진 양초 N개를 모두 사용하여 만들 수 있는 촛불 삼각형의 단수를 출력한다. 만약 삼각형을 만드는 것이 불가능하면 -1을 출력한다.

입력 출력
5
1
3
6
14
762078456028
#1 1
#2 2
#3 3
#4 -1
#5 1234567

 

[소스 코드]

 

#include <stdio.h>
#include <math.h>
int main(void)
{
	int test_case;
	int T;
	long long int candle = 0;
	double tri = 0;
	setbuf(stdout, NULL);
	scanf("%d", &T);
	for (test_case = 1; test_case <= T; ++test_case)
	{
		scanf("%lld", &candle);
		tri = (-1 + sqrt(1 + 8 * candle)) / 2;
		if (tri - (int)tri != 0) {
			tri = -1;
		}
		printf("#%d %.0lf\n", test_case, tri);
	}
	return 0; //정상종료시 반드시 0을 리턴해야 합니다.
}

[느낀 점]

10만개의 테스트 케이스를 1초 안에 통과해야하는데 math의 sqrt때문인지 제한시간 초과가 나왔다.

결국 촛불의 개수를 알고 있다면 몇단인지 확인하려면 K(K+1) = 2N을 풀어야하는데 K를 구하기 위해 근의 공식을 이용하니 루트를 씌워서 안되는 것 같다.

다른 방법이 있는지 확인해보면 좋겠다.

[링크]

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AXGBKzuaPOoDFAXR&categoryId=AXGBKzuaPOoDFAXR&categoryType=CODE

문제

자연수 m이 자연수 n의 약수가 된다는 것은 n을 m으로 나누었을 때, 나머지가 없음을 의미한다. 예를 들어, 6의 약수는 1, 2, 3, 6 네 개가 있고 이의 합은 12이다. 자연수 A와 B가 주어졌을 때, A이상 B 이하의 모든 수에 대해 약수의 합을 다 더한 값을 구하는 프로그램을 작성하시오.

- 제한시간: 전체 테스트 케이스는 20개 이하이며, 전체 수행 시간은 1초 이내. (Java 2초 이내) 
    제한 시간을 초과하면 제출한 소스코드의 프로그램이 즉시 종료되며,
    그때까지 수행한 결과에서 테스트 케이스를 1개 그룹 이상 통과하였더라도 점수는 0점이 됩니다.
    그러나, 제한 시간을 초과하더라도 테스트 케이스를 1개 그룹 이상 통과하였다면 '부분 점수(0< 점수< 만점)'를 받을 수 있으며,
    이를 위해서는, C / C++ 에서 "printf 함수" 사용할 경우, 프로그램 시작부분에서 "setbuf(stdout, NULL);"를 한번만 사용하십시오.
    C++에서는 "setbuf(stdout, NULL);"와 "printf 함수" 대신 "cout"를 사용하고, Java에서는 "System.out.printIn"을 사용하시면,
    제한 시간을 초과하더라도 '부분 점수'를 받을 수 있습니다.                                     ※ 언어별 기본 제공 소스코드 내용 참고
    만약, 제한 시간을 초과하지 않았는데도 '부분 점수'를 받았다면, 일부 테스트 케이스를 통과하지 못한 경우 입니다.

메모리 사용 제한

heap, global, static (총계) : 256MB
stack : 1MB

입력

입력 파일에는 여러 테스트 케이스가 포함될 수 있다.
파일의 첫째 줄에 테스트 케이스의 개수를 나타내는 자연수 T 가 주어지고,
이후 차례로  T 개의 테스트 케이스가 주어진다. (1 ≤ T ≤ 20)
각 테스트 케이스의 입력은 다음과 같은 형식이다.
첫 번째 줄에 수의 범위를 나타내는 두 자연수 A와 B가 공백으로 구분되어 주어진다. (1 ≤ A ≤ B ≤ 5,000)

- 점수 : 최대 10회 제출하여 취득한 각각의 점수 중에서 최대 점수 (만점 100점)
모든 테스트 케이스를 맞추었을 때 만점을 받는다.

출력

각 테스트 케이스의 답을 순서대로 표준출력으로 출력하여야 하며,
각 테스트 케이스마다 첫 줄에는 “Case #C”를 출력하여야 한다. 이때 C는 테스트 케이스의 번호이다.
그 다음 줄에 문제의 정답을 출력한다. A이상 B이하의 모든 수에 대해 약수의 합을 다 더한 값을 출력한다.

푸는 TIP

main함수에 약수를 확인하는 것은 매우 더러워 보이니 따로 함수를 생성해서 사용할 때마다 호출하는 것이 간편하다.

나눠서 나머지가 0이 아니면 약수가 아니므로 '%' 연산자로 약수 판별을 쉽게 해낼 수 있다.

소스코드

#include <stdio.h>
int Answer;
void Devided(int input);
int main(void)
{
	int T, test_case;
	int min = 0, max = 0;
	setbuf(stdout, NULL);
	scanf("%d", &T);
	for (test_case = 0; test_case < T; test_case++)
	{
		Answer = 0;
		scanf("%d %d", &min, &max);
		for (int i = min; i <= max; i++) {
			Devided(i);
		}
		printf("Case #%d\n", test_case + 1);
		printf("%d\n", Answer);
	}

	return 0;//Your program should return 0 on normal termination.
}
void Devided(int input) {
	for (int i = 1; i <= input; i++) {
		if (input % i == 0) {
			Answer += i;
		}
	}
}

느낀 점

SCPC가 곧 다가와서 준비하는 차원으로 코드 그라운드 문제를 풀어보았는데, 첫 번째로 올라와있는걸 풀어서 그런지 백준이나 SW 아카데미에서 푼 문제들에 비해 쉽게 풀렸다.

링크

 

최근 어떤 동영상 플랫폼에서 P채널과 T채널이 구독자 수 1위를 놓고 치열한 경쟁을 벌이고 있다.
영은이는 자신의 주위 사람들은 어떤 채널을 구독하고 있을지 궁금해하여, N명의 사람들에게 아래 두 질문을 하였다.


    -  P채널을 구독하고 있나요?
    -  T채널을 구독하고 있나요?

그 결과, A명이 1번 질문에 ‘네’라고 답했고, B명이 2번 질문에 ‘네’라고 답했다.
이때, P채널과 T채널 모두 구독하고 있는 사람들이 최소 몇 명, 최대 몇 명인지 구하는 프로그램을 작성하라.


 

[입력]
 

첫 번째 줄에 테스트 케이스의 수 T가 주어진다.

각 테스트 케이스의 첫 번째 줄에는 세 개의 정수 N (1 ≤ N ≤ 100), A, B (0 ≤ A, B ≤ N)이 공백 하나를 사이로 두고 순서대로 주어진다.


 

[출력]
 

각 테스트 케이스마다 ‘#x’(x는 테스트케이스 번호를 의미하며 1부터 시작한다)를 출력하고,

P채널과 T채널 모두 구독하고 있는 사람의 수의 최댓값과 최솟값을 공백 하나를 사이로 두고 차례대로 출력한다.

 

[푸는 TIP]

각 분기를 if문으로 모두 나눠버려서 빈틈없이 해결하도록 한다.

[소스 코드]

#include <stdio.h>
int main(void)
{
	int test_case;
	int T;
	int N, A, B;
	int max, min;
	setbuf(stdout, NULL);
	scanf("%d", &T);

	for (test_case = 1; test_case <= T; ++test_case)
	{
		scanf("%d %d %d", &N, &A, &B);
		if (A == B && B == N) {
			max = min = A;
		}
		else if (A == N) {
			max = min = B;
		}
		else if (B == N) {
			max = min = A;
		}
		else { 
			if (A+B <= N) {
				min = 0;
				if (A > B) { 
					max = B;
				}
				else if (A < B) {
					max = A;
				}
				else if(A == B) {
					max = A;
				}
			}
			else { //A+B가 N보다 클경우
				min = A + B - N;
				if (A > B) {
					max = B;
				}
				else if (A < B) {
					max = A;
				}
				else if (A == B) {
					max = A;
				}
			}
		}
		printf("#%d %d %d\n", test_case, max, min);
	}
	return 0; //정상종료시 반드시 0을 리턴해야 합니다.
}

[느낀 점]

SW expert academy에서 거의 처음 푼 문제인데, 테스트 케이스 통과를 못해서 너무 화났지만 계속 논리적으로 빈틈을 찾아보니 빈틈없이 잘 풀 수 있게 되었다.

[문제 링크]

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AXMCXV_qVgkDFAWv&categoryId=AXMCXV_qVgkDFAWv&categoryType=CODE

문제

카지노에서 제일 인기 있는 게임 블랙잭의 규칙은 상당히 쉽다. 카드의 합이 21을 넘지 않는 한도 내에서, 카드의 합을 최대한 크게 만드는 게임이다. 블랙잭은 카지노마다 다양한 규정이 있다.

한국 최고의 블랙잭 고수 김정인은 새로운 블랙잭 규칙을 만들어 상근, 창영이와 게임하려고 한다.

김정인 버전의 블랙잭에서 각 카드에는 양의 정수가 쓰여 있다. 그 다음, 딜러는 N장의 카드를 모두 숫자가 보이도록 바닥에 놓는다. 그런 후에 딜러는 숫자 M을 크게 외친다.

이제 플레이어는 제한된 시간 안에 N장의 카드 중에서 3장의 카드를 골라야 한다. 블랙잭 변형 게임이기 때문에, 플레이어가 고른 카드의 합은 M을 넘지 않으면서 M과 최대한 가깝게 만들어야 한다.

N장의 카드에 써져 있는 숫자가 주어졌을 때, M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 구해 출력하시오.

입력

첫째 줄에 카드의 개수 N(3 ≤ N ≤ 100)과 M(10 ≤ M ≤ 300,000)이 주어진다. 둘째 줄에는 카드에 쓰여 있는 수가 주어지며, 이 값은 100,000을 넘지 않는다.

합이 M을 넘지 않는 카드 3장을 찾을 수 있는 경우만 입력으로 주어진다.

출력

첫째 줄에 M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 출력한다.

푸는 TIP

3중 for문으로 카드 3장을 뽑는 것을 표현한다. 첫번째 i는 0부터, 두번째 j는 i+1부터, 세번째 k는 j+1부터 세기 시작하여 데이터 저장의 끝까지 모든 경우의 수를 체크하고, 근사값과 블랙잭 숫자와의 차이점을 체크해서 동일한 숫자가 나온다면 바로 종료하도록 설계하면 좋을것 같다.

소스코드 (C언어)

더보기

#include <stdio.h>
#include <malloc.h>
#pragma warning(disable : 4996)
int main(void) {
 int card_num = 0, max = 0, nearest = 0, diff = 0; //카드의 개수, 설정한 수, 가장 가까운 수, 차이값
 int temp = 0, diff_temp = 0;
 int* arr = NULL;
 scanf("%d %d", &card_num, &max);
 arr = (int* )malloc(card_num * sizeof(int));
 for (int i = 0; i < card_num; i++) {
  scanf("%d", &arr[i]);
 }
 nearest = arr[0] + arr[1] + arr[2];
 if (max - nearest < 0) {
  diff = nearest - max;
 }
 else {
  diff = max - nearest;
 }
 for (int i = 0; i < card_num - 2; i++) {
  for (int j = i+1; j < card_num - 1; j++) {
   for (int k = j+1; k < card_num; k++) {
    temp = arr[i] + arr[j] + arr[k];
    if (max - temp >= 0 && max - temp < diff) {
     nearest = temp;
     diff = max - temp;
    }
    if (diff == 0) { //만약 동일한 수가 나왔다면(차이값이 0이면) 종료
     printf("%d",max);
     return 0;
    }
   }
  }
 }
 printf("%d", nearest);
 free(arr);
 return 0;
}

실행결과

10개의 카드와 500의 숫자를 넣었을 때, 가장 근사값 497 표현

느낀 점

3중 for문을 어떻게 효율적으로 바꿔보려했는데 아직까지는 좋은 아이디어가 떠오르지 않아서 그대로 사용하였다.

역시 C언어를 오랜만에 사용하는 터라 익숙치 않아 비효율적인 방법으로 해결한 것 같다.

문제 링크

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

+ Recent posts