본문 바로가기

알고리즘

조세푸스 문제

문제링크: 조세푸스 문제


원형으로 원소를 삭제하는 문제이다. 

벡터나 c++라이브러리에서 지원하는 벡터나 리스트를 이용하면 간단히 되지만 다른 라이브러리를 사용하지 않으려고 링크드리스트를 만들어 사용해 보았다. 


- 원형 리스트를 이용한 구현

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
#include <iostream>
using namespace std;
int C, N, K;
 
typedef struct list {
    int elem;
    list *prev;
    list *next;
} List;
 
List *list;
 
List* addNode(List *head, int elem) {
    List *node = (List*)malloc(sizeof(List));
    node->elem = elem;
    head->next = node;
    node->prev = head;
    node->next = NULL;
    return node;
}
void printList(List *head) {
    for (int i = 0; i < N; i++) {
        cout << head->elem << " ";
        head = head->next;
    }
    cout << endl;
}
 
List* removeNode(List* node) {
    List* nextNode;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    nextNode = node->next;
    free(node);
    return nextNode;
}
void solve() {
    // 첫노드
    List *head = (List*)malloc(sizeof(List));
    head->elem = 1;
    head->prev = NULL;
    head->next = NULL;
 
    List *tmp = head;
    for (int i = 2; i <= N; i++) {
        tmp = addNode(tmp, i);
        if (i == N) {
            tmp->next = head;
            head->prev = tmp;
        }
    }
    // printList(head);
    tmp = removeNode(head);
    for (int i = 0; i < (N - 3); i++) {
        for (int j=0; j < (K-1); j++) {
            tmp = tmp->next;
        }
        tmp = removeNode(tmp);    
    }
    int result[2];
    for (int i = 0; i < 2; i++) {
        result[i] = tmp->elem;
        tmp = tmp->next;
    }
    if (result[0> result[1])
        swap(result[0], result[1]);
    cout << result[0<< " " << result[1<< endl;
}
int main() {
    freopen("input.txt""r", stdin);
    cin >> C;
    for (int c = 0; c < C; c++) {
        cin >> N >> K;
        solve();
 
    }
    return 0;
}
cs



- 최적화된 해법 (dp 이용)

K-1번 포인터를 옮기는 대신, (K-1) mod N번 포인터를 옮기는 것이다. 

N명이 남아있을 때, N번 포인터를 옮기면 결국 자기 자신으로 돌아오기 때문이다. 

아래와 같이 dp로 풀 수 있다. 

0, 1 두명이 남았을 때부터 시작해서 N명이 남았을 때, 두명의 index를 찾는 것으로 풀어나가면 된다. 

이해가 쉽지는 않음.. 

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
#include <iostream>
 
using namespace std;
 
int main()
{
    int C;
    cin >> C;
    while (C--)
    {
        // j: last survivor, k: second-last survivor
        int j, k, N, K;
        cin >> N >> K;
 
        // recurrence relation: j(n, k) = { j(n-1, k) + (k-1) } % (n-1) + 1
        j = 0, k = 1;
        for (int i = 2; i < N; i++)
        {
            j = (j + (K-1)) % (i) + 1;
            k = (k + (K-1)) % (i) + 1;
        }
 
        cout << min(j, k) + 1 << " " << max(j, k) + 1 << endl;
    }
 
    return 0;
}
cs