https://www.acmicpc.net/problem/12846

 

12846번: 무서운 아르바이트

성화는 악독하기로 유명한 편의점 사장이다. 그의 편의점에는 특이한 임금 체계를 가지고 있다. 각 날마다 일의 차이때문에 일마다 급여가 정해져 있다. 돈은 당일에 주지 않고 퇴직을 할 때 한

www.acmicpc.net

일을 연속적으로 해야하며, 한 번만 할 수 있습니다.

또한 일을 한 기간동안 가장 작은 일급 * 기간의 돈을 받습니다.

 

가장 작은 일급이 10이고, 5일동안 일해서 받은 50보다

가장 작은 일급이 20이고, 3일동안 일해서 받은 60이 더 많은 이익을 챙길 수 있습니다.

 

이 문제는 히스토그램 문제와 요구하는 것이 같으며,

https://emoney96.tistory.com/139

 

boj 6549 히스토그램에서 가장 큰 직사각형

www.acmicpc.net/problem/6549 6549번: 히스토그램에서 가장 큰 직사각형 입력은 테스트 케이스 여러 개로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있고, 직사각형의 수 n이 가장 처음으로 주

emoney96.tistory.com

https://emoney96.tistory.com/138

 

boj 2104 부분배열 고르기

www.acmicpc.net/problem/2104 2104번: 부분배열 고르기 크기가 N(1≤N≤100,000)인 1차원 배열 A[1], …, A[N]이 있다. 어떤 i, j(1≤i≤j≤N)에 대한 점수는, (A[i]+…+A[j])×Min{A[i], …, A[j]}가 된다. 즉,..

emoney96.tistory.com

두 문제와 동일한 풀이입니다.

 

트리에는 가장 작은 일급을 가진 인덱스를 저장합니다.

init 함수로 전 구간의 최소 인덱스를 저장합니다.

그리고 partition 함수를 통해 해당 구간에서 가장 작은 값을 가지는 인덱스를 query 함수로 찾은 후 최대 이익을 갱신합니다.

해당 인덱스는 가장 작은 값이므로 제외하고, [s ~ idx - 1] [idx + 1 ~ e] 구간으로 나누어서 같은 과정을 반복합니다.

 

 

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>
#include <algorithm>
#define MAX 100001
using namespace std;
typedef long long ll;
 
ll tree[MAX * 4], list[MAX], ans;
int N;
 
void init(int node, int s, int e) {
    if (s == e) {
        tree[node] = s;
        return;
    }
 
    int m = (s + e) / 2;
    init(node * 2, s, m);
    init(node * 2 + 1, m + 1, e);
 
    int l = tree[node * 2];
    int r = tree[node * 2 + 1];
    if (list[l] < list[r]) tree[node] = l;
    else tree[node] = r;
 
    return;
}
 
int query(int node, int s, int e, int l, int r) {
    if (l > e || s > r) return -1;
    if (l <= s && e <= r) return tree[node];
 
    int m = (s + e) / 2;
    int lIdx = query(node * 2, s, m, l, r);
    int rIdx = query(node * 2 + 1, m + 1, e, l, r);
 
    if (lIdx == -1) {
        return rIdx;
    }
    else if (rIdx == -1) {
        return lIdx;
    }
    else {
        return list[lIdx] < list[rIdx] ? lIdx : rIdx;
    }
}
 
void partition(int s, int e) {
    if (s > e) return;
 
    int idx = query(11, N, s, e);
    ans = max(ans, list[idx] * (ll)(e - s + 1));
    partition(s, idx - 1);
    partition(idx + 1, e);
}
 
void func() {
    init(11, N);
    partition(1, N);
 
    cout << ans << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 10167 금광  (0) 2022.09.02
boj 16993 연속합과 쿼리  (2) 2022.08.29
boj 1849 순열  (0) 2021.09.08
boj 14719 빗물  (0) 2021.03.15
boj 2304 창고 다각형  (0) 2021.02.25

www.acmicpc.net/problem/2243

 

2243번: 사탕상자

첫째 줄에 수정이가 사탕상자에 손을 댄 횟수 n(1≤n≤100,000)이 주어진다. 다음 n개의 줄에는 두 정수 A, B, 혹은 세 정수 A, B, C가 주어진다. A가 1인 경우는 사탕상자에서 사탕을 꺼내는 경우이다.

www.acmicpc.net

사탕의 맛이 1 ~ 1000000 까지 있습니다.

저는 맛을 인덱스로 하여 세그먼트 트리를 사용하여 구간 합을 구현하였습니다.

 

A가 1일 때, B는 꺼낼 사탕의 순위입니다.

A가 2일 때, B는 넣는 사탕의 맛이고, C는 갯수이며 양수면 넣는 경우, 음수면 빼는 경우입니다.

 

A = 2가 들어오면 B 인덱스에 C만큼 추가하면 되겠고

A = 1이 들어오면 사탕의 순위를 이분탐색으로 찾습니다.

 

이분탐색은 세그먼트 트리의 값을 이용하여 범위를 찾습니다.

x가 왼쪽 자식노드의 수보다 작거나 같으면 (tree[node * 2] >= x) l ~ m 범위로 내려가고 (더 낮은 구역을 탐색)

x가 왼쪽 자식노드의 수보다 크면 (tree[node * 2 < x) m + 1 ~ r의 범위로 내려갑니다. (더 높은 구역을 탐색)

이 때 m + 1 ~ r의 범위로 이동할 때 오른쪽 자식노드의 순위를 판단해야 하기때문에 x - tree[node * 2] 한 값을 보내줍니다.

 

마지막으로 l == r 구역에 도달하면 사탕을 한개 빼줘야하므로 update를 해주고 찾은 인덱스를 리턴해줍니다.

 

 

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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class Main {
    static final int MAX = 1000000;
    static int tree[] = new int[(MAX + 1* 4];
    static int N;
 
    static void update(int node, int s, int e, int idx, int diff) {
        if (idx < s || idx > e)
            return;
 
        if (s == e) {
            tree[node] += diff;
            return;
        }
 
        tree[node] += diff;
        int m = (s + e) / 2;
        update(node * 2, s, m, idx, diff);
        update(node * 2 + 1, m + 1, e, idx, diff);
    }
 
    static int binarysearch(int node, int l, int r, int x) {
        if (l == r) {
            update(11, MAX, l, -1);
            return l;
        }
 
        int m = (l + r) / 2;
        if (tree[node * 2>= x)
            return binarysearch(node * 2, l, m, x);
        else
            return binarysearch(node * 2 + 1, m + 1, r, x - tree[node * 2]);
    }
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        StringBuffer sb = new StringBuffer();
 
        N = Integer.parseInt(st.nextToken());
        while (N-- > 0) {
            st = new StringTokenizer(br.readLine());
            int type = Integer.parseInt(st.nextToken());
 
            if (type == 1) {
                int a = Integer.parseInt(st.nextToken());
                sb.append(binarysearch(11, MAX, a)).append("\n");
            } else {
                int a = Integer.parseInt(st.nextToken());
                int b = Integer.parseInt(st.nextToken());
 
                update(11, MAX, a, b);
            }
        }
 
        System.out.println(sb.toString());
    }
}
cs

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

boj 14438 수열과 쿼리 17  (0) 2021.02.21
boj 13537 수열과 쿼리 1  (0) 2021.02.21
boj 6549 히스토그램에서 가장 큰 직사각형  (0) 2021.02.19
boj 2104 부분배열 고르기  (0) 2021.02.19
boj 2042 구간 합 구하기  (0) 2021.01.23

https://www.acmicpc.net/problem/2517

 

2517번: 달리기

첫째 줄에는 선수의 수를 의미하는 정수 N이 주어진다. N은 3 이상 500,000 이하이다. 이후 N개의 줄에는 정수가 한 줄에 하나씩 주어진다. 이 값들은 각 선수들의 평소 실력을 앞에서 달리고 있는

www.acmicpc.net

자신 앞에 있는 사람들 중 자신보다 실력이 더 낮은 사람이 몇 명인지 구하는 문제입니다.

이 문제는 두 가지 방법으로 해결하였습니다.

예전에 배운적 있었던 Merge Sort방법과 이 문제의 풀이방법으로 명시되어 있는 Segment Tree 방법으로 문제를 두번 풀어보았습니다.

 

먼저 Merge Sort 방법입니다.

평소의 Merge Sort를 사용했던 것처럼 i번과 j번을 서로 비교해가며 큰 값을 sorted[k]에 저장하는 방식입니다.

 

여기서 기존의 Merge Sort에 추가해야 할 것이 하나 있는데

i번은 앞의 인덱스, j번은 뒤의 인덱스 이므로 j번 인덱스의 값이 앞으로 이동하게 되면 그 차이만큼 rank에서 빼줍니다. (i번이 뒤로가는 것은 무시하셔도 됩니다.)

 

 

C++로 구현한 merge sort방식
JAVA로 구현한 merge sort방식

 

작년에 C++로 동일한 방식으로 해결하였으나 java에서는 TLE를 받았는데

아는 형님께서 StringBuilder라는 것을 알려주셨고, 출력 부분에 이것만 추가하니 AC를 받았습니다.

StringBuilder를 이제 자주 써야겠다는 생각이 드는군요 ㅎㅎ

오늘도 하나 배워갑니다..

 

 

[Merge Sort]

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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int list[][], rank[], sorted[][];
 
    static int N;
 
    static void mergesort(int l, int m, int r) {
        int i = l;
        int k = l;
        int j = m + 1;
 
        while (i <= m && j <= r) {
            if (list[i][0> list[j][0]) {
                sorted[k][0= list[i][0];
                sorted[k][1= list[i][1];
 
                i++;
                k++;
            } else {
                sorted[k][0= list[j][0];
                sorted[k][1= list[j][1];
 
                rank[list[j][1]] -= (j - k);
 
                j++;
                k++;
            }
        }
 
        if (i > m) {
            for (; j <= r; j++, k++) {
                sorted[k][0= list[j][0];
                sorted[k][1= list[j][1];
            }
        } else if (j > r) {
            for (; i <= m; i++, k++) {
                sorted[k][0= list[i][0];
                sorted[k][1= list[i][1];
            }
        }
 
        for (int t = l; t <= r; t++) {
            list[t][0= sorted[t][0];
            list[t][1= sorted[t][1];
        }
    }
 
    static void merge(int l, int r) {
        if (l != r) {
            int m = (l + r) / 2;
            merge(l, m);
            merge(m + 1, r);
            mergesort(l, m, r);
        }
    }
 
    static void print() {
        StringBuilder sb = new StringBuilder();
        
        for (int i = 1; i <= N; i++) {
            sb.append(rank[i]+"\n");
        }
        System.out.println(sb.toString());
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        list = new int[N + 1][2];
        sorted = new int[N + 1][2];
        rank = new int[N + 1];
 
        for (int i = 1; i <= N; i++) {
            st = new StringTokenizer(br.readLine());
            list[i][0= Integer.parseInt(st.nextToken());
            list[i][1= i;
            rank[i] = i;
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        merge(1, N);
        print();
    }
}
cs

 

다음은 Segment Tree 방식입니다.

이 방식은 구간 합을 구하는 방식으로 해결할 수 있습니다.

먼저 list 배열을 실력의 오름차순으로 정렬한 후, 순서대로 인덱스를 트리에 저장해주시면 됩니다.

 

실력 순으로 query 후 update를 진행하기 때문에 query로 트리를 탐색할 때는 모든 값들이 자신보다 실력이 낮은 값들입니다.

여기서 인덱스가 자신보다 앞에 있는 갯수만 찾으시면 됩니다..

 

merge sort방식으로 구현
segment tree방식으로 구현

문제에 명시되어있는 풀이방법은 Segment Tree인데 Merge Sort방식으로 풀이한게 더 빠른 시간이 나왔습니다.

제가 로직을 잘못 구현했을 수도 있지만 여러방식으로 시도해봐야겠습니다..

 

 

[Segment Tree]

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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int list[][], tree[], rank[];
    static int N;
 
    static int query(int node, int s, int e, int l, int r) {
        if (l <= s && e <= r)
            return tree[node];
        if (s > r || e < l)
            return 0;
 
        int m = (s + e) / 2;
        return query(node * 2, s, m, l, r) + query(node * 2 + 1, m + 1, e, l, r);
    }
 
    static void update(int node, int s, int e, int idx) {
        if (idx < s || idx > e)
            return;
 
        tree[node]++;
        int m = (s + e) / 2;
        if (s != e) {
            update(node * 2, s, m, idx);
            update(node * 2 + 1, m + 1, e, idx);
        }
    }
 
    static void func() {
        StringBuffer sb = new StringBuffer();
        for (int i = 1; i <= N; i++) {
            int ans = query(11, N, 1, list[i][1]);
 
            rank[list[i][1]] = list[i][1- ans;
            update(11, N, list[i][1]);
        }
 
        for (int i = 1; i <= N; i++) {
            sb.append(rank[i]+"\n");
        }
        System.out.println(sb.toString());
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        list = new int[N + 1][2];
        rank = new int[N + 1];
        tree = new int[N * 4];
        for (int i = 1; i <= N; i++) {
            st = new StringTokenizer(br.readLine());
            list[i][0= Integer.parseInt(st.nextToken());
            list[i][1= i;
        }
 
        Arrays.sort(list, new Comparator<int[]>() {
            public int compare(int[] a, int[] b) {
                if (a[0> b[0])
                    return 1;
                else
                    return -1;
            }
        });
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
    }
}
cs

'algorithm > data-structure' 카테고리의 다른 글

boj 5639 이진 검색 트리  (0) 2021.01.24
boj 1991 트리 순회  (0) 2021.01.22
boj 11003 최솟값 찾기  (0) 2021.01.22
boj 1517 버블 소트  (0) 2021.01.22
boj 3425 고스택  (0) 2021.01.22

+ Recent posts