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



1L에 1km씩 갈 수 있고, 목적지까지 갈 수 있냐의 문제.



N 이 10000밖에 되질 않아서 DP로도 풀린다.


그리디는 글쎄 생각이 나질 않는다.




이 문제는 우선순위 큐로 풀었는데,


우선 주유소를 거리를 기준으로 sort하고(문제가 약았다)


내가 갈 수 있는 범위 내의 주유소의 기름을 max heap에 다가 넣어 준다.


그리고 max heap의 첫 번째의 기름을 더해서 내가 갈 수 있는 범위를 늘리며, 


그 기름양은 pop해준다.


그 범위 안에 있는 모든 주유소의 기름양을 max heap에 넣는다.


그리고 다시 max heap 첫 번 째 기름을 더해서 내가 갈 수 있는 범위를 늘리며 그 기름양은 pop하는 것을 반복한다.



최종적으로 목적지의 거리만큼 나의 기름양이 누적되었으면, 성공! 기름을 더해준 횟수를 출력.


안되면 -1을 출력한다.



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
#define _CRT_SECURE_NO_WARNINGS
 
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
 
int n;
int nowGas, destDist;
class station {
public:
    int gas, dist;
    station() {};
    station(int gas, int dist) : gas(gas), dist(dist) {}
    bool operator <(station& cmp) {
        return this->dist < cmp.dist;
    }
};
 
station st[10001];
int main() {
    freopen("input.txt""r", stdin);
    freopen("output.txt""w", stdout);
 
    scanf("%d"&n);
 
    for (int i = 0; i < n; i++) {
        scanf("%d%d"&st[i].dist , &st[i].gas);
    }
 
    sort(st, st + n);
    scanf("%d%d"&destDist, &nowGas);
 
    priority_queue<int> pq;
    int maxIdx = 0, num = 0;
    while (true) {
        if (nowGas >= destDist) break;
 
        while (maxIdx < n && st[maxIdx].dist <= nowGas){
            pq.push(st[maxIdx].gas);
            maxIdx++;
        }
        if (pq.empty()) break;
 
        nowGas += pq.top();
        pq.pop();
        num++;
    }
    
    if (nowGas < destDist) {
        cout << -1 << endl;
    }
    else {
        cout << num << endl;
    }
    return 0;
}
cs


'Tech Study > BOJ' 카테고리의 다른 글

[백준 1655번] 가운데를 말해요  (0) 2018.01.02
Posted by SangJoKim

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



주어지는 입력을 소트한다고 했을 때, 그 중간 값을 뽑아내며, 


전체 짝수가 주어지면 중간 값 2개 중 더 작은 애를 뽑아내는 문제다.




이 문제를 해결하는 방법을 생각해보자.


먼저 드는 생각은 balanced tree를 만들어 중간 위치를 찾으면 그만이다.


하지만, treap이나 red-black tree를 어느 세월에 만들어 낼 것인가....


문제의 유형처럼 heap으로 해결해보자.




heap으로 해결하는 방법이 선뜻 떠오르지 않는다.



먼저 2개의 heap을 만들어 낸다.



maxHeap과 minHeap인데


주어진 수열을 sort한 다음, 앞 절반을 maxHeap에,  뒷 절반을 minHeap에 넣으면 


다음 2가지 성질이 성립된다.



1. maxHeap의 size는 minHeap size보다 같거나 1크다.


2. maxHeap의 첫 원소는 minHeap의 첫 원소보다 작거나 같다.



자 input이 주어지면, 


input을 maxHeap, minHeap 번갈아서 넣는다. maxHeap 먼저.



그리고 maxHeap의 첫 원소와 minHeap의 첫 원소를 비교해서


maxHeap이 더 크면, 2개의 위치를 바꾸어준다.




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
#define _CRT_SECURE_NO_WARNINGS
 
#include <iostream>
#include <cstdio>
#include <queue>
 
using namespace std;
 
int main() {
    freopen("input.txt""r", stdin);
    freopen("output.txt""w", stdout);
 
    int n; scanf("%d"&n);
    priority_queue<int> maxHeap, minHeap;
    
    for (int i = 0; i < n; i++) {
        int inp; scanf("%d"&inp);
    
        if (maxHeap.size() == minHeap.size()) {
            maxHeap.push(inp);
        }
        else {
            minHeap.push(-inp);
        }
        if (!minHeap.empty() && !maxHeap.empty() && -minHeap.top() < maxHeap.top()) {
            int a = maxHeap.top(), b = -minHeap.top();
            maxHeap.pop(), minHeap.pop();
            maxHeap.push(b);
            minHeap.push(-a);
        }
        printf("%d\n", maxHeap.top());
    }
    return 0;
}
cs


'Tech Study > BOJ' 카테고리의 다른 글

[백준 1826] 연료 채우기  (0) 2018.01.02
Posted by SangJoKim

Priority Queue는 Queue의 응용으로, 각 node의 우선순위를 매겨서


우선순위가 높은 순으로 혹은 낮은 순으로 node를 관리하는 개념이다.



우선 순위 큐를 어떻게 구현을 할 것인가에 대해 naive하게 생각해보면, 


우선 순위 개수 만큼의 queue를 만들고, 각 우선 순위가 존재하는지 여부의 bit field를 만들어서


해결 할 수도 있다. (실제로 linux의 task scheduling에서 사용했던 기법이다)





하지만 많이 사용하는 방법으로는


Balanced Binary Search Tree를 응용하여 구현하는 것이다.




이 자료구조는 Heap이라고 하는 자료구조 인데, 이 자료구조를 이용하면, 


원소를 추가(push)하는 일과


원소를 삭제(pop)하는 일이 각각 logN만큼의 시간이 걸린다.







Heap의 구현



max heap(우선 순위가 항상 높은 것이 pop되는 heap)을 예로 들어 heap의 규칙에 대해 말하자면,



부모 노드는 항상 자식 노드보다 우선 순위가 높아야 한다는 것이다.





사실 heap의 구현은 그리 복잡하지 않다. 


우선 배열을 하나 만든다.


1
vector<int> heap;
cs




여기에서 push와 pop을 구현해주면 되는 것이다.



먼저 push부터 살펴보자



1) push



처음할 일은 push된 data를 heap 배열의 맨 끝에 다가 넣는 것이다.



그리고 그 원소의 부모와 비교를 하여 부모가 더 작다면, 위치를 바꾸어 준다 (max heap이므로 부모가 더 커야한다)


만약 옮겼다면, 옮긴 위치에서 부모와 비교하여 부모가 더 작다면, 위치를 바꾸어 준다.


루트이거나, 부모가 더 클 때까지 이와 같은 작업을 반복한다.


이를 그림으로 나타내면 다음과 같다

                                                a                                                 b

                                               c





이때, push된 원소가 최대로 많이 이동하는 것은 루트까지 올라오는 것이고 heap은 balanced tree이기 때문에 


logN만큼의 시간이 걸린다. 따라서 push연산은 O(logN)이 걸리게 된다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void push(vector<int>& heap, int newValue){
 
    //우선 새로운 원소를 맨 끝에 다가 추가해준다.
    heap.push_back(newValue);
 
    //맨끝의 원소의 위치를 얻는다.
    int idx = heap.size() - 1;
 
    //원소가 root가 될 때까지 부모와 비교한다. 
    //만약 부모보다 더 크면 바꿔주고 그 원소의 위치도 갱신해준다. 
    while(idx > 0 && heap[(idx - 1/ 2< heap[idx]){
        swap(heap[idx], heap[idx - 1/ 2);
        idx = (idx - 1/ 2
    }
}
cs




잠깐 여기서 이야기 안 한 것이 있는데, 자신의 위치가 idx라면, 부모의 위치는 (idx - 1) / 2이다


이는 root가 0부터 시작할 때이다. 



만약 배열을 만들고 index = 1부터 root를 만든다면 부모노드는 그냥 idx / 2 이면 된다(직접 그림을 그려서 확인하길 바란다)



그리고 heap[( idx - 1) / 2] < heap[idx] 라는 조건에서 만약


min heap을 구현 하고 싶으면 >로 바꾸어 주면된다.


왜냐하면 min heap은 부모가 더 작으면 되기 때문이다.






2) 최대 원소 꺼내기



원소를 꺼내는 것 자체는 쉽다


맨위의 노드 즉, root를 꺼내면 되기 때문이다.


문제는 그 다음이다. root를 꺼내고 나서도 heap을 유지해야하기 때문이다.


여기에서 시간이 걸린다.




한 가지 방법으로, 마지막 값을 root로 올린 다음, 자식 node와 비교하여, 자식이 더 작을 때까지 


그 위치를 바꾸어 주는 것이다. min heap이라면, 자식이 더 클 때까지(max heap = 부모가 자식보다 항상 크다 / min heap = 부모가 자식보다 항상 작다)




종만북의 code를 조금 응용한 것이다.



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
int pop(vector<int>& heap){
 
    //root는 꺼내진다.
    int ret = heap[0];
 
    //맨 마지막 원소를 넣고,
    heap[0= heap.back();    
 
    //마지막을 빼준다.
    heap.pop_back();
 
    //root에서 부터.
    int here = 0;
 
    while(true){
 
        //왼쪽자식, 오른쪽 자식이다.
        int left = here * 2 + 1, right = here * 2 + 2;
 
        int next = here;
 
        //왼쪽자식이 있고, 왼쪽 자식이 더 크면(max heap은 부모가 더 크다)
        //왼쪽자식은 바꿔줄 후보가 된다.
        if(left < heap.size() && heap[next] < heap[left])
            next = left;
 
        //마찬가지로 오른쪽 자식이 있고 오른쪽 자식이 더크면
        //오른쪽 자식은 바꿔줄 후보가 된다.
        //왼쪽자식, 오른쪽 자식 중 더 큰 것이 바꿔줄 최종 후보가 되는 것이다.
        if(right < heap.size() && heap[next] < heap[right])
            next = right;
 
        //바꿔줄 후보가 없으면 그만 둔다.
        if(next == here) break;
 
        //바꿔준다.
        swap(heap[here], heap[next]);
        here = next;
    }
    return ret;
}
cs

 




24번째 줄과 30번째 줄에서 min이냐 max냐에따라서 대소 관계가 바뀌는 것을 잊지말자.









힙정렬(heap sort)




주어진 원소를 heap을 이용해 push한 다음 pop을 해서 하나씩 꺼내면, 그 것이 바로 heap sort가 되고


push할 때 NlogN, pop할 때 NlogN만큼의 시간이 걸린당.




Posted by SangJoKim
이전버튼 1 이전버튼

블로그 이미지
SangJoKim

태그목록

공지사항

Yesterday
Today
Total

달력

 « |  » 2024.5
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

최근에 올라온 글

최근에 달린 댓글

글 보관함