BOJ : https://www.acmicpc.net/problem/9202

github : https://github.com/junho0956/Algorithm/blob/master/9202/9202/%EC%86%8C%EC%8A%A4.cpp

 

2가지 방법으로 해결했습니다.

trie 자료구조 복습겸 작성해봤고, trie 없이 dfs 하는 방식으로 해봤습니다.

 

trie 자료구조를 이용한 방법

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//Boggle
 //일반적인 DFS 문제인데 현재 탐색을 기준 문자열이 존재하는지 없는지 확인하는 작업이 필요하다
 //DFS의 탐색때 마다 모든 문자열을 확인 작업하게되면 TLE가 될 것이다
 //문자열을 빠르게 찾기 위해서는 수업시간에 배운 trie 자료구조를 사용해야한다.
 //문자트리 라고 생각하면 된다
 
#include <fstream>
#include <iostream>
#include <string>
#include <cstring>
#include <set>
using namespace std;
 
ifstream fcin("large_hand.in");
ofstream fcout("large_hand.out");
 
int w, b;
int visit[4][4];
string board[4];
set<string> key;
int score[9= { 0,0,0,1,1,2,3,5,11 };
int my[8= { -1,-1,-1,0,0,1,1,1 };
int mx[8= { -1,0,1,-1,1,-1,0,1 };
 
struct trie {
    // 알파벳A~Z까지에 대한 배열
    trie* node[26];
    // 단어의 종료를 알리는 배열
    bool finish;
 
    // new 를 위한 생성자
    trie() {
        memset(this->node, NULLsizeof(this->node));
        this->finish = false;
    }
 
    // 스트링에 대한 트라이트리 만들기
    // 현재 포인터에 대한 다음 포인터가 없다면 연결을 위해 만들어준다
    void insert(string s) {
        trie* p = this;
        for (int i = 0; i < s.length(); i++) {
            int index = s[i] - 'A';
            if (p->node[index] == NULL)
                p->node[index] = new trie();
            p = p->node[index];
        }
        // 단어의 종료를 알려줘야한다
        p->finish = true;
    }
 
    void search(int y, int x, string s) {
        // 단어가 8글자이하라고 했다
        if (s.length() > 8return;
 
        visit[y][x] = 1;
        s += board[y][x];
 
        trie* check = this->node[board[y][x] - 'A'];
        // 현재 포인터에 대해 방금 추가한 단어가 없다 -> 연결안되있다면 반환
        if (check == NULL) {
            visit[y][x] = 0;
            return;
        }
 
        // 중복처리를 위해 set 처리
        if (check->finish) key.insert(s);
 
        for (int i = 0; i < 8; i++) {
            int yy = y + my[i];
            int xx = x + mx[i];
            if (yy >= 0 && yy < 4 && xx >= 0 && xx < 4 && visit[yy][xx] == 0) {
                check->search(yy, xx, s);
            }
        }
        // dfs 에 의한 방문처리
        visit[y][x] = 0;
    }
};
 
int main() {
    ios::sync_with_stdio(0), fcin.tie(0), fcout.tie(0);
 
    // trie의 루트노드
    trie* root = new trie();
 
    fcin >> w;
    while (w--) {
        string str;
        fcin >> str;
        // 단어추가
        root->insert(str);
    }
 
    fcin >> b;
    while (b--) {
        key.clear();
        for (int i = 0; i < 4; i++)
            fcin >> board[i];
 
        // 0.0 부터 3.3 까지 전부 탐색해준다
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                root->search(i, j, "");
 
        string longstr = "";
        int total = 0, findNum = key.size();
        for (string po : key) {
            total += score[po.length()];
 
            if (longstr.length() == po.length())
                longstr = longstr < po ? longstr : po;
            else if (longstr.length() < po.length())
                longstr = po;
        }
 
        fcout << total << " " << longstr << " " << findNum << "\n";
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

 

trie 없이 기본 dfs 로만 구현

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 <iostream>
#include <set>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
 
int w, b;
string board[4];
bool visit[4][4], isFind;
string isWant;
set<string> key;
int score[9= { 0,0,0,1,1,2,3,5,11 };
int my[8= { -1,-1,-1,0,0,1,1,1 };
int mx[8= { -1,0,1,-1,1,-1,0,1 };
string v[300001];
 
void search(int y, int x, int index) {
    if (isFind || index >= isWant.size()) return;
    if (isWant[index] != board[y][x]) return;
 
    visit[y][x] = true;
 
    if (index == isWant.size() - 1 && board[y][x] == isWant[index]) {
        key.insert(isWant);
        isFind = true;
        visit[y][x] = false;
        return;
    }
 
    for (int i = 0; i < 8; i++) {
        int yy = y + my[i];
        int xx = x + mx[i];
        if (yy >= 0 && yy < 4 && xx >= 0 && xx < 4 && visit[yy][xx] == 0) {
            search(yy, xx, index + 1);
            if (isFind) {
                visit[y][x] = false;
                return;
            }
        }
    }
 
    visit[y][x] = false;
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
 
    cin >> w;
    for (int i = 0; i < w; i++cin >> v[i];
 
    cin >> b;
    while (b--) {
        key.clear();
        for (int i = 0; i < 4; i++)
            cin >> board[i];
        for (int k = 0; k < w; k++) {
            bool t = false;
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    if (board[i][j] == v[k][0]) {
                        isFind = false;
                        isWant = v[k];
                        int num = key.size();
                        search(i, j, 0);
                        if (num != key.size()) { t = truebreak; }
                    }
                }
                if (t) break;
            }
        }
        string longest = "";
        int total = 0, cnt = key.size();
        for (string at : key) {
            if (longest.length() == at.length())
                longest = longest < at ? longest : at;
            else if (longest.length() < at.length())
                longest = at;
            total += score[at.length()];
        }
        cout << total << " " << longest << " " << cnt << "\n";
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

'algorithm > BOJ' 카테고리의 다른 글

BOJ 15922번 아우으 우아으이야!!  (0) 2020.02.05
BOJ 2170번 선 긋기  (0) 2020.02.05
BOJ 10999번 구간 합 구하기2  (0) 2020.02.05
BOJ 2042번 구간 합 구하기  (0) 2020.02.05
BOJ 3055번 탈출  (0) 2020.02.04

BOJ : https://www.acmicpc.net/blog/view/26

 

설명 : https://www.acmicpc.net/blog/view/26

 

백준님 정말 '갓' 그자체 이십니다

 

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
#include <cstdio>
#include <algorithm>
#define MAX_N 1000000
typedef long long ll;
using namespace std;
ll n, m, k, seg[4 * MAX_N], lazy[4 * MAX_N], a, b, c, d;
void update_lazy(ll node, ll x, ll y) {
    if (!lazy[node])
        return;
    seg[node] += (y - x + 1)*lazy[node];
    if (x != y) {
        lazy[node * 2+= lazy[node];
        lazy[node * 2 + 1+= lazy[node];
    }
    lazy[node] = 0;
}
ll update(ll lo, ll hi, ll val, ll node, ll x, ll y) {
    update_lazy(node, x, y);
    if (y < lo || hi < x)
        return seg[node];
    if (lo <= x&&<= hi) {
        lazy[node] += val;
        update_lazy(node, x, y);
        return seg[node];
    }
    ll mid = (x + y) >> 1;
    return seg[node] = update(lo, hi, val, node * 2, x, mid) + update(lo, hi, val, node * 2 + 1, mid + 1, y);
}
ll query(ll lo, ll hi, ll node, ll x, ll y) {
    update_lazy(node, x, y);
    if (y < lo || hi < x)
        return 0;
    if (lo <= x&&<= hi)
        return seg[node];
    ll mid = (x + y) >> 1;
    return query(lo, hi, node * 2, x, mid) + query(lo, hi, node * 2 + 1, mid + 1, y);
}
int main() {
    scanf("%lld%lld%lld"&n, &m, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%lld"&a);
        update(i, i, a, 11, n);
    }
    for (int i = 0; i < m + k; i++) {
        scanf("%lld"&a);
        if (a == 1) {
            scanf("%lld%lld%lld"&b, &c, &d);
            update(b, c, d, 11, n);
        }
        else {
            scanf("%lld%lld"&b, &c);
            printf("%lld\n", query(b, c, 11, n));
        }
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

'algorithm > BOJ' 카테고리의 다른 글

BOJ 2170번 선 긋기  (0) 2020.02.05
BOJ 9202번 Boggle  (0) 2020.02.05
BOJ 2042번 구간 합 구하기  (0) 2020.02.05
BOJ 3055번 탈출  (0) 2020.02.04
BOJ 4888번 문시티 건설  (0) 2020.02.03

BOJ : https://www.acmicpc.net/problem/2042

github : https://github.com/junho0956/Algorithm/blob/master/2042/2042/%EC%86%8C%EC%8A%A4.cpp

 

세그먼트트리를 이용하여 해결하였다.

 

세그먼트트리 정리 : https://junho0956.tistory.com/180

 

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
#include <iostream>
using namespace std;
 
typedef long long ll;
ll arr[1000004];
ll seg[3000004];
int n, m, k;
 
ll segment(int left, int right, int node) {
    if (left == right)
        return seg[node] = arr[left];
 
    int mid = (left + right) / 2;
    seg[node] += segment(left, mid, node * 2);
    seg[node] += segment(mid + 1, right, node * 2 + 1);
 
    return seg[node];
}
 
void update(int node, int left, int right, int change, int val) {
 
    if (!(left <= change && change <= right)) return;
 
    seg[node] += (val - arr[change]);
 
    if (left != right) {
        int mid = (left + right) / 2;
        update(node * 2, left, mid, change, val);
        update(node * 2 + 1, mid + 1, right, change, val);
    }
 
}
 
ll sum(int node, int left, int right, int s, int e) {
    if (left > e || right < s) return 0;
    if (s <= left && e >= right) return seg[node];
 
    int mid = (left + right) / 2;
    return sum(node * 2, left, mid, s, e) + sum(node * 2 + 1, mid + 1, right, s, e);
}
 
int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++cin >> arr[i];
 
    seg[1= segment(1, n, 1);
    int testcase = m + k;
    while (testcase--) {
        int a, b, c;
        cin >> a >> b >> c;
        if (a == 1) update(11, n, b, c), arr[b] = c;
        else cout << sum(11, n, b, c) << "\n";
    }
 
    return 0;
}
 
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter

'algorithm > BOJ' 카테고리의 다른 글

BOJ 9202번 Boggle  (0) 2020.02.05
BOJ 10999번 구간 합 구하기2  (0) 2020.02.05
BOJ 3055번 탈출  (0) 2020.02.04
BOJ 4888번 문시티 건설  (0) 2020.02.03
BOJ 17387, 17386번 선분 교차  (0) 2020.02.03

세그먼트 트리에 대해서 이해한대로 정리해보자!

 

세그먼트 트리는 전체 구간 중에서 일부 구간에 대한 변경이 있을 때

그 변경으로 인한 변화를 빠르게 알기 위해서 사용한다 ==> 구간탐색을 위해서 사용한다

 

임의의 배열에 1 2 3 4 5 라는 값이 있고, 이에 대한 세그먼트 트리를 만들기 위해서는

1 2 3 4 5 를 리프노드로 하는 일정범위(구간)에 대한 정보를 알아야 한다

 

배열을 이용해서 세그먼트트리를 표현해보자!

세그먼트 트리의 대표적인 문제는 구간 합을 해결하는 문제를 예로 들 수 있다.

가장 기본적인 구간 합을 예로 들어서 세그먼트 트리를 설명하려고 한다

 

세그먼트 트리의 가장 윗 부분의 인덱스(노드번호)를 1로 지정하게 되면,

임의의 노드의 자식노드는 *2, *2+1 의 인덱스를 가지게 된다(완전트리의 형식이기 때문)

 

루트 노드인 1번 노드는 1,2,3,4,5 에 대한 모든 범위[1,5] 에 대한 정보를 가진다.

루트 노드의 자식노드인 2(1*2)번, 3(1*2+1)번 노드는 [1,5] 의 범위에 대한 중간값을 기준으로

각각 [1,3], [4,5] 에 대한 정보를 가지게 된다

 

-----------------------------------------------------------------------------------------------------------------------------------

가장 먼저 재귀를 통해 세그먼트 트리를 만드는 함수를 살펴보자!

1
2
3
4
5
6
7
8
9
10
ll segment(int left, int right, int node) {
    if (left == right)
        return seg[node] = arr[left];
 
    int mid = (left + right) / 2;
    seg[node] += segment(left, mid, node * 2);
    seg[node] += segment(mid + 1, right, node * 2 + 1);
 
    return seg[node];
}
 

ll 은 long long 을 의미한다.

left , right 는 현재 노드번호(node = 위에서 말한 배열인덱스)가 가지고 있는 정보이다

첫 호출 때는 segment(1,N,1) 으로 호출했다고 볼 수 있다.

 

left == right 를 만족하게 되면 node번쨰 인덱스의 배열에는 세그먼트트리의 리프노드 값인 실제 값이 들어갈 공간!

그게 아니라면 (left+right)/2 를 통해 재귀를 들어가주면서 += 를 해줌으로써 자식에 대한 총합을 가지게 된다!

 

이런식으로 모든 범위에 대한 세그먼트 트리를 만들 수 있다.

 

-------------------------------------------------------------------------------------------------------------------------------

원하는 위치의 값을 다른 값으로 바꾸는 update에 대해서 알아보자

** update 는 세그먼트 트리의 리프노드인 원하는 위치의 값을 바꿈과 동시에

리프노드와 관련된 모든 노드의 정보를 바꾸는 작업이다

 

지금 당장 원하는 위치의 값이 세그먼트 트리의 몆번째 노드에 있는지 알고 있다면

/2를 해가면서 리프노드부터 루트노드까지 올라가면서 값을 변경해주면 된다.

하지만 보통? 문제들은 세그먼트트리의 몆번째 노드에 원하는 값이 있는지 알 수 없다.

 

그러므로 루트노드부터 쭉 내려가면서 원하는 위치의 리프노드를 찾아가면서 값을 바꿔주면 된다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
void update(int node, int left, int right, int change, int val) {
 
    if (!(left <= change && change <= right)) return;
 
    seg[node] += (val - arr[change]);
 
    if (left != right) {
        int mid = (left + right) / 2;
        update(node * 2, left, mid, change, val);
        update(node * 2 + 1, mid + 1, right, change, val);
    }
 
}
 

인자를 순서대로 살펴보면

node => 현재 노드번호(배열인덱스)

left, right => 현재 노드가 가지고 있는 범위

change => 바꾸고싶은 리프노드 위치

val => 바꿀 값

 

첫번째 if문을 살펴보자

 

나는 현재 3번째 값을 5로 바꾸고 싶다고 가정했을 때,

현재 탐색 중인 노드의 범위가 3번째위치를 포함하지 않는다면 빠르게 손절한다는 의미이다

[1,5] 의 범위에는 3번째위치가 포함되지만

[1,2], [4,5] 의 범위에는 3번째위치가 포함되지 않는다.

결국 left <= change && change <= right 를 만족하면 현재 세그먼트에 대한 정보를 변경해도 되는 것!

 

두번째 if문의 의미는 현재 탐색 중인 노드가 리프노드인지 아닌지를 확인하는 것이다

left == right이면 리프노드를 의미하게 되니 더이상 update를 수행할 필요가 없는 것이고,

left != right이면 자식노드에 대한 정보를 update 해줘야한다!

 

-----------------------------------------------------------------------------------------------------------------------------------

 

구간 합을 구하는 방법을 살펴보자

다음 예시를 빠르게 이해한다면 코드 역시 빠르게 이해할 수 있다.

 

리프노드가 아닌 현재 노드는 리프노드들의 정보를 가지고 있다고 했었다.

현재 나는 구간[2,5] 에 대한 정보를 알고싶다.

[1,5] 의 범위는 필요한 정보를 가지고 있는가?

정답은 x 이다. 1에 대한 정보를 가지고 있기 때문 => 불필요한 정보!

[4,5] 의 범위는 필요한 정보를 가지고 있는가?

정답은 o 이다. [2,5] 중에서 [4,5] 에 대한 정보를 가지고 있기 때문이다.

** 불필요한 정보는 버리고 필요한 범위만 가져오는 것이 세그먼트 트리의 구간을 이용하는 기본개념!

 

** 현재 범위를 left, right 내가 알고 싶은 범위를 s, e 라고 한다면

s 가 left 보다 작거나 같고, e 가 right 보다 크거나 같다면

현재 확인 중인 노드는 알고싶은 [s,e] 에 대한 정보를 가지고 있으니 더이상 탐색할 필요가 없게 된다.

 

** ex) [2,5] 의 정보를 알고자 하는데 현재 노드가 [4,5]를 가지고 있다면

2<=4 && 5 <= 5 이면 모든 정보를 가지고 있음을 의미함 **

 

1
2
3
4
5
6
7
ll sum(int node, int left, int right, int s, int e) {
    if (left > e || right < s) return 0;
    if (s <= left && e >= right) return seg[node];
 
    int mid = (left + right) / 2;
    return sum(node * 2, left, mid, s, e) + sum(node * 2 + 1, mid + 1, right, s, e);
}
 

** 첫번째 if 문은 현재 알고 싶은 범위가 노드가 가진 정보와 관련이 없다면 탐색종료 한다는 의미이다.

** 두번째 if 문은 현재 노드가 가진 범위가 내가 알고싶은 범위에 대한 정보를 모두 포함한다는 의미이다.

 

** 그렇지 않다면 재귀를 통해 탐색해주면 된다.

'algorithm > algorithm' 카테고리의 다른 글

볼록껍질 선분 처리하기  (0) 2020.02.28
유클리드 호제법  (0) 2020.02.28
선분 교차 판별하기  (0) 2020.02.02
삼분 탐색(Ternary search)  (0) 2020.01.16
LCS  (0) 2019.07.04

+ Recent posts