크기가 N×M인 지도가 존재한다. 지도의 오른쪽은 동쪽, 위쪽은 북쪽이다. 이 지도의 위에 주사위가 하나 놓여져 있으며, 주사위의 전개도는 아래와 같다. 지도의 좌표는 (r, c)로 나타내며, r는 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로부터 떨어진 칸의 개수이다.
2 4 1 3 5 6
주사위는 지도 위에 윗 면이 1이고, 동쪽을 바라보는 방향이 3인 상태로 놓여져 있으며, 놓여져 있는 곳의 좌표는 (x, y) 이다. 가장 처음에 주사위에는 모든 면에 0이 적혀져 있다.
지도의 각 칸에는 정수가 하나씩 쓰여져 있다. 주사위를 굴렸을 때, 이동한 칸에 쓰여 있는 수가 0이면, 주사위의 바닥면에 쓰여 있는 수가 칸에 복사된다. 0이 아닌 경우에는 칸에 쓰여 있는 수가 주사위의 바닥면으로 복사되며, 칸에 쓰여 있는 수는 0이 된다.
주사위를 놓은 곳의 좌표와 이동시키는 명령이 주어졌을 때, 주사위가 이동했을 때 마다 상단에 쓰여 있는 값을 구하는 프로그램을 작성하시오.
주사위는 지도의 바깥으로 이동시킬 수 없다. 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다.
입력
첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다.
둘째 줄부터 N개의 줄에 지도에 쓰여 있는 수가 북쪽부터 남쪽으로, 각 줄은 서쪽부터 동쪽 순서대로 주어진다. 주사위를 놓은 칸에 쓰여 있는 수는 항상 0이다. 지도의 각 칸에 쓰여 있는 수는 10을 넘지 않는 자연수 또는 0이다.
마지막 줄에는 이동하는 명령이 순서대로 주어진다. 동쪽은 1, 서쪽은 2, 북쪽은 3, 남쪽은 4로 주어진다.
출력
이동할 때마다 주사위의 윗 면에 쓰여 있는 수를 출력한다. 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다.
이 문제는 삼성 SW역량 테스트에 나왔던 기출 문제이다.
그런데 난이도는 그렇게 높지 않은 문제라고 생각한다. 단순히 문제를 이해하고 그대로 구현하는 시뮬레이션 이다.
알고리즘
1.동서남북에 따라 주사위의 위치(x,y)와 값을 변경한다.
2.지도의 상태에 따라 동작
새로운 주사위 위치의 지도값이 0이면 주사위 바닥값을 지도에 옮긴다.
바닥값이 0이아닐 경우 주사위 바닥에 값을 옮긴 후 지도의 값을 0으로 만든다
위 순서에 따라 구현하면 쉽게 구현할 수 있다.
후기
이 문제를 보고 동 서 남 북에 따라 주사위 약도의 값이 어디로 이동하는지 노트에 그려보면서 풀었고, 쉽게 풀렸다 ㅎㅎ
코드
#include<iostream>
using namespace std;
int N, M, x, y, K;
int map[21][21];
int rl[] = { 0,0,0,-1,1 }; // 인덱스 1부터 동 서 북 남
int cl[] = { 0,1,-1,0,0 };// 인덱스 1부터 동 서 북 남
int val[7] = { 0, }; //3이 위 6이 아래
int main() {
cin >> N >> M >> x >> y >> K;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> map[i][j];
}
}
for (int i = 0; i < K; i++) {
int move = 0;
cin >> move;
int newx = x+rl[move];
int newy = y+cl[move];
if (newx < 0 || newy < 0 || newx >= N || newy >= M) continue;
int temp = 0;
if (move == 1) {
temp=val[3];
val[3] = val[2];
val[2] = val[6];
val[6] = val[4];
val[4] = temp;
}
else if (move == 2) {
temp = val[6];
val[6] = val[2];
val[2] = val[3];
val[3] = val[4];
val[4] = temp;
}
else if (move == 3) {
temp = val[6];
val[6] = val[1];
val[1] = val[3];
val[3] = val[5];
val[5] = temp;
}
else {
temp = val[3];
val[3] = val[1];
val[1] = val[6];
val[6] = val[5];
val[5] = temp;
}
if (map[newx][newy] == 0) {
map[newx][newy] = val[6];
}
else {
val[6] = map[newx][newy];
map[newx][newy] = 0;
}
cout << val[3]<<endl;
x = newx;
y = newy;
}
}
각 질문은 두 정수 S와 E로 나타낼 수 있으며, S번째 수부터 E번째 까지 수가 팰린드롬을 이루는지를 물어보며, 명우는 각 질문에 대해 팰린드롬이다 또는 아니다를 말해야 한다.
예를 들어, 홍준이가 칠판에 적은 수가 1, 2, 1, 3, 1, 2, 1라고 하자.
S = 1, E = 3인 경우 1, 2, 1은 팰린드롬이다.
S = 2, E = 5인 경우 2, 1, 3, 1은 팰린드롬이 아니다.
S = 3, E = 3인 경우 1은 팰린드롬이다.
S = 5, E = 7인 경우 1, 2, 1은 팰린드롬이다.
자연수 N개와 질문 M개가 모두 주어졌을 때, 명우의 대답을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 수열의 크기 N (1 ≤ N ≤ 2,000)이 주어진다.
둘째 줄에는 홍준이가 칠판에 적은 수 N개가 순서대로 주어진다. 칠판에 적은 수는 100,000보다 작거나 같은 자연수이다.
셋째 줄에는 홍준이가 한 질문의 개수 M (1 ≤ M ≤ 1,000,000)이 주어진다.
넷째 줄부터 M개의 줄에는 홍준이가 명우에게 한 질문 S와 E가 한 줄에 하나씩 주어진다.
출력
총 M개의 줄에 걸쳐 홍준이의 질문에 대한 명우의 답을 입력으로 주어진 순서에 따라서 출력한다. 팰린드롬인 경우에는 1, 아닌 경우에는 0을 출력한다.
먼저 팰린드롬이 무엇인지 알아보자.
- 팰린드롬이란 앞으로 읽으나 뒤로 읽으나 같은 단어를 뜻한다. 예를들어 aoa pop 같은게 팰린드롬이다.
이 문제에서는 숫자로 적용하였으므로 121 , 2332 같은 것이 팰린드롬이 되겠다.
풀이
개념은 간단하다. 하지만 시간을 줄이는 것이 관건 이므로 dp를 사용했다.
1 2 1 3 1 2 1 을 예로 들어 보자.(가장 앞 인덱스를 1 마지막 인덱스를 7)
i는 시작 인덱스 , j는 끝 인덱스 라고 정한다.
<1개의 숫자>
i와 j가 같다면 팰린드롬이다. 즉, 하나의 숫자만 있다면 그것은 팰린드롬이다.
<2개의 숫자>
숫자가 2개이므로 두 숫자가 같다면 팰린드롬이다.
이 두가지 경우를 코드로 나타내면 아래와 같다.
for (int i = 1; i <= N; i++) {
dp[i][i] = 1;
dp[i][i + 1] = ary[i] == ary[i + 1] ? 1 : 0;
}
시작 인덱스와 끝 인덱스의 차이가 1인 범위의 dp값은 위와 같은 코드로 구했다.
(시작과 끝의 차이가 1인 범위 -> ex) dp[1][2] )
그럼 시작 인덱스와 끝 인덱스의 차이가 2인 곳 부터 구하면 되겠다.
<시작과 끝의 차이가 2이상일 때 팰린드롬인 경우>
1. 시작 인덱스의 값과 끝 인덱스의 값이 같다.
2. dp[시작 인덱스 +1][끝 인덱스 -1] 이 팰린드롬이다.
이 것을 코드로 나타내보면.
for (int i = 2; i < N; i++) {
for (int j = 1; j <= N; j++) {
int k = i + j;
if (k > N) break;
if (ary[j] == ary[k] && dp[j + 1][k - 1] == 1) {
dp[j][k] = 1;
}
}
}
이런식으로 나타낼 수 있다.
전체 코드
#include<iostream>
using namespace std;
int N, M;
int ary[2002];
int dp[2002][2002];
int main() {
scanf("%d",&N);
for (int i = 1; i <= N; i++) {
scanf("%d",&ary[i]);
}
for (int i = 1; i <= N; i++) {
dp[i][i] = 1;
if(ary[i]==ary[i+1]){
dp[i][i+1]=1;
}
}
for (int i = 2; i < N; i++) {
for (int j = 1; j <= N; j++) {
int k = i + j;
if (k > N) break;
if (ary[j] == ary[k] && dp[j + 1][k - 1] == 1) {
dp[j][k] = 1;
}
}
}
scanf("%d",&M);
for (int i = 0; i < M; i++) {
int r, c;
scanf("%d %d",&r,&c);
printf("%d\n",dp[r][c]);
}
}
후기
이 문제를 풀면서 상당히 짜증?이 났다...
분명히 아무리 생각해도 맞게 푼 것 같은데 자꾸만 시간초과가 나는 것이다....
이렇게 바꿔보고 저렇게 바꿔 보다가 1시간이 지나고... 결국 다른 분들의 코드를 참고하려 검색했다.
근데.. 이게 무엇?? 내가 푼 방식이랑 완전히 똑같은 코드가 올라와 있었다..
왜 안될까? 생각하다가 그분의 입출력 함수는 scanf()와 printf() 였다. 나는 cin cout을 사용하고 있었다.
상근이는 오락실에서 바구니를 옮기는 오래된 게임을 한다. 스크린은 N칸으로 나누어져 있다. 스크린의 아래쪽에는 M칸을 차지하는 바구니가 있다. (M<N) 플레이어는 게임을 하는 중에 바구니를 왼쪽이나 오른쪽으로 이동할 수 있다. 하지만, 바구니는 스크린의 경계를 넘어가면 안 된다. 가장 처음에 바구니는 왼쪽 M칸을 차지하고 있다.
스크린의 위에서 사과 여러 개가 떨어진다. 각 사과는 N칸중 한 칸의 상단에서 떨어지기 시작하며, 스크린의 바닥에 닿을때까지 직선으로 떨어진다. 한 사과가 바닥에 닿는 즉시, 다른 사과가 떨어지기 시작한다.
바구니가 사과가 떨어지는 칸을 차지하고 있다면, 바구니는 그 사과가 바닥에 닿을 때, 사과를 담을 수 있다. 상근이는 사과를 모두 담으려고 한다. 이때, 바구니의 이동 거리의 최솟값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N과 M이 주어진다. (1 ≤ M < N ≤ 10) 둘째 줄에 떨어지는 사과의 개수 J가 주어진다. (1 ≤ J ≤ 20) 다음 J개 줄에는 사과가 떨어지는 위치가 순서대로 주어진다.
#include<iostream>
using namespace std;
int N, M, j;
int le = 1,res;
int main() {
cin >> N >> M;
cin >> j;
for (int i = 0; i < j; i++) {
int val;
cin >> val;
if(val<le){
res += le - val;
le = val;
}
else if(val>(le + M -1)){
res += val - (le + M - 1);
le = val - M + 1;
}
}
cout << res << endl;
}