본문 바로가기

공부/알고리즘 문제풀이

[알고스팟] CLOCKSYNC

반응형

링크 : https://algospot.com/judge/problem/read/CLOCKSYNC



문제

judge-attachments/d3428bd7a9a425b85c9d3c042b674728/clocks.PNG

그림과 같이 4 x 4 개의 격자 형태로 배치된 16개의 시계가 있다. 이 시계들은 모두 12시, 3시, 6시, 혹은 9시를 가리키고 있다. 이 시계들이 모두 12시를 가리키도록 바꾸고 싶다.

시계의 시간을 조작하는 유일한 방법은 모두 10개 있는 스위치들을 조작하는 것으로, 각 스위치들은 모두 적게는 3개에서 많게는 5개의 시계에 연결되어 있다. 한 스위치를 누를 때마다, 해당 스위치와 연결된 시계들의 시간은 3시간씩 앞으로 움직인다. 스위치들과 그들이 연결된 시계들의 목록은 다음과 같다.

00, 1, 2
13, 7, 9, 11
24, 10, 14, 15
30, 4, 5, 6, 7
46, 7, 8, 10, 12
50, 2, 14, 15
63, 14, 15
74, 5, 7, 14, 15
81, 2, 3, 4, 5
93, 4, 5, 9, 13

시계들은 맨 윗줄부터, 왼쪽에서 오른쪽으로 순서대로 번호가 매겨졌다고 가정하자. 시계들이 현재 가리키는 시간들이 주어졌을 때, 모든 시계를 12시로 돌리기 위해 최소한 눌러야 할 스위치의 수를 계산하는 프로그램을 작성하시오.

입력

첫 줄에 테스트 케이스의 개수 C (<= 30) 가 주어진다. 
각 테스트 케이스는 한 줄에 16개의 정수로 주어지며, 각 정수는 0번부터 15번까지 각 시계가 가리키고 있는 시간을 12, 3, 6, 9 중 하나로 표현한다.

출력

각 테스트 케이스당 한 줄을 출력한다. 시계들을 모두 12시로 돌려놓기 위해 눌러야 할 스위치의 최소 수를 출력한다. 만약 이것이 불가능할 경우 -1 을 출력한다.

예제 입력

2
12 6 6 6 6 6 12 12 12 12 12 12 12 12 12 12 
12 9 3 12 6 6 9 3 12 9 12 9 12 12 6 6

예제 출력

2
9


어떻게 풀까?


복잡해보이는 이 문제를 어떻게 풀 수 있을지 생각해봅시다.


우선, 스위치를 누를때 시계의 움직임이다. 시계는 스위치를 누르면, 3 - 6 - 9 - 12 - 3 ... 의 순서로 움직입니다. 

즉, 스위치를 3번 이상 누르면 이전까지 누른 의미가 사라져 버린다는 것입니다!


이 사실을 깨닫고 나면, 

10개의 스위치를 

0번 누르는 경우, 

1번 누르는 경우, 

2번 누르는 경우, 

3번 누르는 경우를 모두 탐색하는 '완전탐색' 기법을 사용하면 문제를 해결할 수 있을 것이라는 결론을 얻을 수 있습니다.


그렇다면 시간은 얼마나 걸릴까요?


스위치는 모두 10개이고, 스위치 마다 경우의 수가 4개 이므로, 4^10 = 2^20이 된다. 2^10을 1000 이라고 보면, 거의 1000000번의 계산을 한다고 생각하면 됩니다. 

이는, 문제에서 주어진 시간 내에 풀릴 수 있을만한 범위입니다!


10중 for문을 이용해서 문제를 해결할 수 있지만, 재귀를 이용해서 계산해봤습니다. 코드는 아래와 같습니다!


코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<stdio.h>
#include<string.h>
 
int clocks[16];
int cntNum[10];
const int swNum[10= { 3,4,4,5,5,4,3,5,5,5 };
const int swch[10][5= { {0,1,2}, {3,7,9,11}, {4,10,14,15}, {0,4,5,6,7}, {6,7,8,10,12}, {0,2,14,15}, {3,14,15}, {4,5,7,14,15}, {1,2,3,4,5}, {3,4,5,9,13} };
int swCnt[10];
const int INF = 987654321;
int min;
 
int check()
{
    int clockTemp[16];
 
    memcpy(clockTemp, clocks, sizeof(clocks));
 
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < swNum[i]; j++)
        {
            clockTemp[swch[i][j]]+= cntNum[i];
        }
    }
 
    for (int i = 0; i < 16; i++)
    {
        if (clockTemp[i] % 4return 0;
    }
    return 1;
}
 
int switchOn(int cnt,int now)
{
    if (cnt >= min) return 0;
 
    if (now == 10)
    {
        if (check())
        {
            min = min > cnt ? cnt : min;
            return 1;
        }
        
        return 0;
    }
 
    int res = 0;
 
    for (int i = 0; i < 4; i++)
    {
        cntNum[now] = i;
        res += switchOn(cnt + i, now + 1);
    }
    return res;
}
 
int CLOCKSYNC()
{
    int c;
    scanf("%d\n"&c);
 
    while (c--)
    {
        memset(swCnt, 0sizeof(swCnt));
        min = INF;
 
        for (int i = 0; i < 16; i++)
        {
            scanf("%d \n", clocks + i);
            clocks[i] = (clocks[i] / 3) % 4;
        }
 
        if (switchOn(0,0))
        {
            printf("%d\n", min);
        }
        else
        {
            printf("%d\n"-1);
        }
    }
    return 0;
}
cs





PLUS) 위의 코드는 900ms정도의 시간이 걸렸는데, 답을 보니 0ms의 시간이 걸린 코드가 있었습니다. 한번 이를 분석하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio.h>
#include <limits.h>
 
int switches[10][6= {
    { 0,1,2,-1, },
    { 3,7,9,11,-1, },
    { 4,10,14,15,-1, },
    { 0,4,5,6,7,-1, },
    { 6,7,8,10,12,-1, },
    { 0,2,14,15,-1, },
    { 3,14,15,-1,},
    { 4,5,7,14,15,-1, },
    { 1,2,3,4,5,-1, },
    { 3,4,5,9,13,-1, } };
 
int solve_clock_order[10= {8,11,13,6,10,7,4,1,3,14};
int solve_switch_order[10= {4,1,9,3,2,7,8,0,6,5};
 
int check_order[6= {0,2,5,9,12,15};
int pairs[10= {1,11,10,6,8,14,3,7,4,13};
int clocks[16];
 
int main()
{
    int c;
    scanf("%d"&c);
 
    for (int i = 0; i < c; i++)
    {
        for (int j = 0; j < 16; j++)
        {
            int tmp;
            scanf("%d"&tmp);
 
            switch (tmp)
            {
                case 12:
                    clocks[j] = 0;
                    break;
                case 3:
                    clocks[j] = 1;
                    break;
                case 6:
                    clocks[j] = 2;
                    break;
                case 9:
                    clocks[j] = 3;
                    break;
            }
        }
 
        int total_cnt = 0;
 
        for(int j = 0; j < 10; j++)
        {
            int cnt = (4 - clocks[solve_clock_order[j]])%4;
            for(int k = 0; switches[solve_switch_order[j]][k] != -1; k++)
            {
                clocks[switches[solve_switch_order[j]][k]] = (clocks[switches[solve_switch_order[j]][k]] + cnt) % 4;
            }
            total_cnt += cnt;
        }
 
        bool possible = true;
        for(int j = 0; j < 6; j++)
        {
            if(clocks[check_order[j]] != 0)
            {
                possible = false;
                break;
            }
        }
 
        if(possible)
        {
            printf("%d\n", total_cnt);
        }
        else
        {
            printf("-1\n");
        }
    }
}
 
cs


위의 코드에서 핵심은 3가지 입니다. 바로


int solve_clock_order[10= {8,11,13,6,10,7,4,1,3,14};
int solve_switch_order[10= {4,1,9,3,2,7,8,0,6,5};
int check_order[6= {0,2,5,9,12,15};


이 부분입니다. 우선, 스위치를 확인해봅시다.


int switches[10][6= {
    { 0,1,2,-1, },
    { 3,7,9,11,-1, },
    { 4,10,14,15,-1, },
    { 0,4,5,6,7,-1, },
    { 6,7,8,10,12,-1, },
    { 0,2,14,15,-1, },
    { 3,14,15,-1,},
    { 4,5,7,14,15,-1, },
    { 1,2,3,4,5,-1, },
    { 3,4,5,9,13,-1, } };


우선, 스위치 10개 중에서 딱 한번 등장하는 시계를 봅시다. 바로 4번 스위치의 8, 2번 스위치의 11, 5번 스위치의 13이다.


한 마디로, 8번 시계가 3시라면 4번 스위치를 3번 클릭하는 방법만이 8번 시계를 12시로 만들 수 있는 것입니다.


이런식으로 시계의 시간에 따라서 누를 수 밖에 없는 스위치들을 찾아 순서를 정하면, 위의 solve_siwtch_order 가 됩니다.


그 스위치와 연관되는 시계의 번호는 solve_clock_order가 됩니다.


스위치가 10개이고, 시계가 16개 이므로 모든 스위치를 눌러본 다음에 나머지 6개의 시계에 대해서는 답이 맞는지 확인을 해야합니다.


그 6개의 시계가 바로 check_order인 것입니다.


만약, 확인하는 시계들이 12시가 안된다면, 결국 저 스위치들을 눌러야하는데 그러면 결국 시계의 시간들이 다시 꼬여버리는 결과가 됩니다.


따라서, 10개의 스위치를 누른 다음에 나머지 6개의 시계가 12시를 가르키지 않는다면 결국 모든 시계를 12시로 만드는 방법이 없다는 것이고, -1을 출력하는 것입니다!

반응형

'공부 > 알고리즘 문제풀이' 카테고리의 다른 글

[알고스팟] 외발 뛰기  (0) 2018.06.12
[알고스팟] 팬 미팅  (0) 2018.06.11
[알고스팟] 울타리 잘라내기  (0) 2018.06.11
[알고스팟] 쿼드 트리 뒤집기  (0) 2018.06.11
[SW Expert Academy] Code battle!  (0) 2018.06.06