

첫째 줄에는 스위치 개수가 주어진다. 스위치 개수는 100 이하인 양의 정수이다. 둘째 줄에는 각 스위치의 상태가 주어진다. 켜져 있으면 1, 꺼져있으면 0이라고 표시하고 사이에 빈칸이 하나씩 있다. 셋째 줄에는 학생수가 주어진다. 학생수는 100 이하인 양의 정수이다. 넷째 줄부터 마지막 줄까지 한 줄에 한 학생의 성별, 학생이 받은 수가 주어진다. 남학생은 1로, 여학생은 2로 표시하고, 학생이 받은 수는 스위치 개수 이하인 양의 정수이다. 학생의 성별과 받은 수 사이에 빈칸이 하나씩 있다.
스위치의 상태를 1번 스위치에서 시작하여 마지막 스위치까지 한 줄에 20개씩 출력한다. 예를 들어 21번 스위치가 있다면 이 스위치의 상태는 둘째 줄 맨 앞에 출력한다. 켜진 스위치는 1, 꺼진 스위치는 0으로 표시하고, 스위치 상태 사이에 빈칸을 하나씩 둔다.
문제의 요구사항은 크게 3가지로 나눌 수 있다.
초기 상태: N개의 스위치 상태가 0(꺼짐) 또는 1(켜짐)으로 주어진다.
학생별 로직:
최종 출력: 모든 학생의 조작이 끝난 스위치 상태를 '한 줄에 20개씩' 출력한다.
접근 방식
2.1. 자료구조 및 기본 세팅
스위치 배열: 문제에서 스위치 번호가 1번부터 시작(1-based)하므로, 인덱스 관리를 편하게 하기 위해 int[N+1] 크기로 배열을 선언했다.
토글 함수: 스위치 상태를 바꾸는 toggle() 함수를 따로 분리했다.
// 0이면 1로, 1이면 0으로 바꾸는 함수
static void toggle(int pos, int[] arr) {
if (arr[pos] == 1) {
arr[pos] = 0;
} else {
arr[pos] = 1;
}
}
2.2. 남학생 로직 (🐞 첫 번째 함정)
요구사항은 "받은 수의 배수"였다. 처음에는 for (int i = p.pos; i <= N; i = i 2)라고 잘못 작성했다. 예제 입력(N=8, 남학생=3)에서는 3, 6만 해당되어 i2와 i+p.pos의 결과가 우연히 같았고, 이게 '틀렸습니다'의 첫 번째 원인이었다.
// 잘못된 코드 (거듭제곱)
// for (int i = p.pos; i <= N; i = i * 2) { ... }
// 수정된 코드 (배수)
if (p.gender == 1) {
for (int i = p.pos; i <= N; i += p.pos) {
toggle(i, arr);
}
}
2.3. 여학생 로직 (꼼꼼한 경계값 체크)
이 문제의 핵심 로직.
} else { // 여학생
toggle(p.pos, arr); // 1. 중심은 일단 토글
int lPos = p.pos;
int rPos = p.pos;
while (true) {
lPos--;
rPos++;
// 3. 경계값 체크 (가장 중요)
if (lPos < 1 || rPos > N) {
break;
}
// 4. 대칭 체크
if (arr[lPos] != arr[rPos]) {
break;
}
// 5. 통과 시 토글
toggle(lPos, arr);
toggle(rPos, arr);
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
class Point {
int gender;
int pos;
public Point(int gender, int pos) {
this.gender = gender;
this.pos = pos;
}
}
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int[] arr = new int[N + 1];
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
st = new StringTokenizer(br.readLine());
int students = Integer.parseInt(st.nextToken());
Point[] student = new Point[students];
for (int i = 0; i < students; i++) {
st = new StringTokenizer(br.readLine());
int gender = Integer.parseInt(st.nextToken());
int pos = Integer.parseInt(st.nextToken());
student[i] = new Point(gender, pos);
}
for (Point p : student) {
if (p.gender == 1) {
for (int i = p.pos; i <= N; i = i + p.pos) {
toggle(i, arr);
}
} else {
toggle(p.pos, arr);
int lPos = p.pos;
int rPos = p.pos;
while (true) {
lPos--;
rPos++;
if (lPos < 1 || rPos > N) {
break;
}
if (arr[lPos] != arr[rPos]) {
break;
}
toggle(lPos, arr);
toggle(rPos, arr);
}
}
}
for (int i = 1; i <= N; i++) {
System.out.print(arr[i] + " ");
if (i % 20 == 0) {
System.out.println();
}
}
}
static void toggle(int pos, int[] arr){
if(arr[pos] == 1) {
arr[pos] = 0;
}
else{
arr[pos] = 1;
}
}
}
"예제 케이스만 믿지 말자."
내가 실행 결과가 '틀렸습니다'를 받고도 이유를 몰랐던 것은, 남학생로직이 틀렸음에도 불구하고 예제 입력이 너무 운 좋게 내 오류와 정답의 결과가 겹쳤기 때문이다. 만약 N=10이었다면 스위치 때문에 바로 알았겠지만 예제 케이스만 확인하고 오류를 발견하기 힘들어졌다.
"경계값, 출력 형식 등 꼼꼼함이 실력이다."
PCCP 시험에서도 인덱스 오류로 고생했는데, 이번 여학생 로직에서 lPos < 1 같은 경계값을 체크하는 것이 얼마나 중요한지 다시 느꼈다.
시물레이션/구현 문제는 화려한 알고리즘이 아니라, 문제의 요구사항을 단 하나도 빠뜨리지 않고 꼼꼼하게 코드로 옮기는 능력이 핵심이라는 것을 배운 문제였다.