[NYPC 2021_예선문제] 레이스 기록 검증

신형석·2022년 5월 25일
0

알고리즘 풀이

목록 보기
26/41

이전 문제보다는 조금 코딩 난이도가 올라간 문제이다. 문제 전문은 다음과 같다:

정리하면,

  1. 게임 시작 후, 60초가 안되서 끝나면 오류 (종료 t - 시작 t < 60)
  2. 게임 시작을 하지 않았는데 끝이 나버리면 오류 (0보다 1이 먼저 입력된 경우)
  3. 게임 시작을 하고 끝이 나지 않았는데 새로운 게임이 시작되면 오류 (0 다음에 0이 입력된 경우
  4. 게임 시작을 했는데 끝을 내지 않으면 오류 (0이 입력된 후 1이 입력되지 않은 경우)

이렇게 4가지의 오류가 있다.

짠 코드를 보면서 설명하도록 하겠다.

#include <stdio.h>
#include <stdlib.h>

typedef struct log_file {
	int time;
	int user;
	int race;
}Log;

int main() {
	int user_num, log_num;
	scanf("%d %d", &user_num, &log_num);
	Log* inputlog = (Log*)malloc(sizeof(Log) * log_num);
	int* user_arr = (int*)calloc(user_num, sizeof(int));
	int* user_status = (int*)malloc(sizeof(int) * user_num);
	for (int i = 0; i < user_num; i++) {
		user_status[i] = -1;
	}
	for (int i = 0; i < log_num; i++) {
		scanf("%d %d %d", &inputlog[i].time, &inputlog[i].user, &inputlog[i].race);
	}
	int i;
	for (i = 0; i < log_num; i++) {
		if (user_status[inputlog[i].user - 1] == inputlog[i].race) { // 현재 상태와 로그의 상태가 같은 경우
			printf("NO");
			break;
		}
		else if (user_status[inputlog[i].user - 1] == 0 && inputlog[i].race == 1){ // 현재 상태 = 0, 로그 상태 = 1 -> 레이스를 끝낸 경우
			if (inputlog[i].time - user_arr[inputlog[i].user - 1] < 60) { // 레이스를 끝냈는데, 그 차이가 60초 미만일 경우
				printf("NO");
				break;
			}
			user_status[inputlog[i].user - 1] = inputlog[i].race;
		}
		else { // 위의 경우가 모두 아니면, user_arr, user_status에 적절한 값을 대입한다. 
			user_arr[inputlog[i].user - 1] = inputlog[i].time;
			user_status[inputlog[i].user - 1] = inputlog[i].race;
		}
	}
	int j;
	if (i == log_num) { 
		for (j = 0; j < user_num; j++) {
			if (user_status[j] != 1) { // 끝난 상태가 1이 아닌 경우
				printf("NO");
				break;
			}
		}
		if (j == user_num) { // 모든 조건을 만족하고 나왔을 때, YES를 출력한다. 
			printf("YES");
		}
	}
	free(inputlog);
	free(user_arr);
	free(user_status);
	return 0;
}

테스트해보지 않아서 정확한 코드인지는 알 수 없지만, 이러한 코드를 만들게 되었다

typedef struct log_file {
	int time;
	int user;
	int race;
}Log;

이 부분은 구조체 배열을 만들기 위한 구조체이다. Log는 별칭으로 쓰인다.

Log* inputlog = (Log*)malloc(sizeof(Log) * log_num);
int* user_arr = (int*)calloc(user_num, sizeof(int));
int* user_status = (int*)malloc(sizeof(int) * user_num);

각 배열을 동적 할당으로 구분한다.
inputlog 배열은, 로그 상태인 t, i, s를 입력받는 배열이다.
user_arr 배열은, 각 index가 유저 번호 - 1이고, 값은 로그 값의 시간을 갖는 배열이다.
user_status 배열은, 각 유저의 상태, 즉 게임을 시작했는지 끝냈는지를 받는 배열이다. user_status 배열은 그 바로 밑의 문에서 -1로 초기화해주었는데, 그 이유는 뒤쪽에 나온다.

if (user_status[inputlog[i].user - 1] == inputlog[i].race) { // 현재 상태와 로그의 상태가 같은 경우
	printf("NO");
	break;
}

inputlog[i].user는 입력받은 로그에서의 user 번호를 뜻한다. 배열에 값을 저장할 때 우리는 기본적으로 0부터 저장하고, user 값은 1부터 시작한다. 그러므로 1을 빼준 값이 user_status 배열의 index가 된다. inputlog[i].race 값은, 입력받은 로그에서의 레이스 시작 여부를 뜻한다. 이는 0 또는 1로 되어있고, 처음 user_status 배열을 초기화할 때 -1로 초기화했으므로, 우리는 처음 값이 비교될 때 NO를 출력하고 빠져나올 일은 없다고 생각하면 된다.

else if (user_status[inputlog[i].user - 1] == 0 && inputlog[i].race == 1){ // 현재 상태 = 0, 로그 상태 = 1 -> 레이스를 끝낸 경우
	if (inputlog[i].time - user_arr[inputlog[i].user - 1] < 60) { // 레이스를 끝냈는데, 그 차이가 60초 미만일 경우
		printf("NO");
		break;
	}
	user_status[inputlog[i].user - 1] = inputlog[i].race;
}

위의 코드와 이어지는 코드이다. 만약 현재 상태가 0이고, 로그에서 1의 상태를 받는다면 레이스를 시작하고 제대로 끝났다는 뜻이므로, else if 문 안으로 들어오게 된다. 만약, 끝나긴 끝났지만 그 차이가 60초 미만이라면, 그 바로 밑의 if문에 걸려서 break 문을 만나게 되지만, 그렇지 않는다면 user_status에서의 유저 index에 현재 상태인 1을 넣어주게 된다.

else { // 위의 경우가 모두 아니면, user_arr, user_status에 적절한 값을 대입한다. 
	user_arr[inputlog[i].user - 1] = inputlog[i].time;
	user_status[inputlog[i].user - 1] = inputlog[i].race;
}

위 if와 else if문의 경우가 모두 아니라면, 새로운 값을 대입해준다. user_arr에는 시작 시간을 넣어주고, user_status에는 시작 상태, 즉 0을 대입해준다.

if (i == log_num) { 
	for (j = 0; j < user_num; j++) {
		if (user_status[j] != 1) { // 끝난 상태가 1이 아닌 경우
			printf("NO");
			break;
		}
	}
	if (j == user_num) { // 모든 조건을 만족하고 나왔을 때, YES를 출력한다. 
		printf("YES");
	}
}

위의 if 문에서 걸러지지 않는 것들이 있다. 그것이 바로, 시작 상태인 0을 입력받은 후 종료 상태인 1을 입력받지 않는 것들이다. 이런 것들을 걸러내기 위해, 우리는 user_status를 한번 훑어본다. 모든 user_status가 1이면, 모든 유저가 종료 상태이기 때문에 YES를 자신 있게 출력해주면 된다.

이 문제를 조금 쉽게 접근하려면, 기본적으로 유저의 상태는 0과 1을 반복해서 나오고, 끝은 무조건 1이어야 한다는 것을 인지하여야 한다. 0 다음에 0이 나오는 것도 안되고, 1 다음에 1이 나오는 것도 안되며, 1 다음에 0이 나온 후 종료되는 것도 안된다. 이것을 잘 이용하면, 조금 더 간단한 코드가 나올 수도 있을 것 같다.

0개의 댓글