[적용한 알고리즘]

세그먼트 트리, LIS

 

[아이디어]

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

오래전부터 LIS 를 쓰면 풀리겠다고 생각했는데

도저히 N^2 의 DP로는 안풀려 잠깐 포기하고 있었던 문제였는데,

최근에 세그먼트 트리를 이용해서 풀 수 있다고 하여 

블로그를 보고 열심히 구현해봤다.

(http://kks227.blog.me/220791986409)

NlogN 으로 구현 가능한게 세그먼트 트리와 이분탐색이라는데, 

이분탐색을 이용하는 방법도 고민해봐야겠다.

 

<코드>

#include <cstdio>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

bool compare (pair<int, int> a, pair<int, int> b){
    if (a.first == b.first){ //첫번째 비교 대상이 같다면
        return a.second > b.second; //두번째 애를 비교해 큰 놈을 뒤로 보내준다.
    }
    else {
        return a.first < b.first;
    } // 오름차순으로 정렬해주세요
}

void update(vector<int> &tree, int node, int start, int end, int index, int delta){
    if (index < start || index > end) return; // 범위 밖에 있는 경우
    tree[node] = max(tree[node], delta);
    if (start != end){ //리프 노드가 아닌 경우
        int mid = (start + end) / 2;
        update(tree, node * 2, start, mid, index, delta);
        update(tree, node * 2 + 1, mid + 1, end, index, delta);
    }
}
// 내가 구하고 싶은 값의 범위 : left ~ right, 현재 체크 하고 있는 노드의 범위 : start ~ end
int getMAX(vector<int> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start) { // 만약에 포함이 안되어있는 경우
        return -1;
    }
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return max(getMAX(tree, node * 2, start, mid, left, right), getMAX(tree, node * 2 + 1, mid + 1, end, left, right));
}

int main(){
    int n;
    scanf("%d", &n);
    vector<pair<int, int>> a;
    int tree_height = (int)ceil(log2(n));
    int tree_size = (1 << (tree_height + 1));
    vector<int> tree(tree_size);

    for (int i = 0; i < n; i++){
        int x; scanf("%d", &x);
        a.push_back(make_pair(x, i));
    }

    sort(a.begin(), a.end(), compare);

    for (int i = 0; i < n; i++){
        int idx = a[i].second;
        int temp_max = getMAX(tree, 1, 0, n - 1, 0, idx);
        update(tree, 1, 0, n - 1, idx, temp_max + 1);
    }

    printf("%d\n", n - tree[1]);
    return 0;
}

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

[백준] 1753번 최단경로  (0) 2019.11.27
[백준] 12738번 가장 긴 증가하는 부분 수열 3  (0) 2019.11.23
[백준] 2268번 수들의 합  (0) 2019.11.23
[백준] 11505번 구간 곱 구하기  (0) 2019.11.23
[백준] 1275번 커피숍2  (0) 2019.11.23

[적용한 알고리즘]

세그먼트 트리

 

[아이디어]

내용

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

내용

 

<코드>

#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;

long long construct(vector<long long> &a, vector<long long> &tree, int node, int start, int end){
    if (start == end){
        return tree[node] = a[start]; // 리프 노드인 경우 자기 자신을 등록 시켜야함.
    }
    else {
        int mid = (start + end) / 2; // 아니라면 자기 밑에 자식을 더해야 함.
        return tree[node] = construct(a, tree, node * 2, start, mid) + construct(a, tree, node * 2 + 1, mid + 1, end);
    }
}

void update(vector<long long> &tree, int node, int start, int end, int index, long long delta){
    if (index < start || index > end) return; // 범위 밖에 있는 경우
    tree[node] += delta;
    if (start != end){ //리프 노드가 아닌 경우
        int mid = (start + end) / 2;
        update(tree, node * 2, start, mid, index, delta);
        update(tree, node * 2 + 1, mid + 1, end, index, delta);
    }
}
// 내가 구하고 싶은 값의 범위 : left ~ right, 현재 체크 하고 있는 노드의 범위 : start ~ end
long long sum(vector<long long> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start) { // 만약에 포함이 안되어있는 경우
        return 0;
    }
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return sum(tree, node * 2, start, mid, left, right) + sum(tree, node * 2 + 1, mid + 1, end, left, right);
}

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    vector<long long> a(n);
    int tree_height = (int)ceil(log2(n));
    int tree_size = (1 << (tree_height + 1));
    vector<long long> tree(tree_size);

    construct(a, tree, 1, 0, n - 1);

    while (m--){
        int oper;
        scanf("%d", &oper);

        if (oper == 1){
            int x;
            long long y;
            scanf("%d %lld", &x, &y);

            long long delta = y - a[x - 1];
            a[x - 1] = y;
            update(tree, 1, 0, n - 1, x - 1, delta);
        }//update

        if (oper == 0){
            int left, right;
            scanf("%d %d", &left, &right);
            if (left > right) swap(left, right);
            printf("%lld\n", sum(tree, 1, 0, n - 1, left - 1, right - 1));
        }//sum
    }

    return 0;
}

[적용한 알고리즘]

세그먼트 트리

 

[아이디어]

내용

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

구간 합 구하기처럼 update 함수를 구성하면 안된다. 

앞으로 update 함수는 이렇게 써야지

 

<코드>

#include <cstdio>
#include <vector>
#include <cmath>
#define mod 1000000007
using namespace std;

long long construct(vector<long long> &a, vector<long long> &tree, int node, int start, int end){
    if (start == end){
        return tree[node] = a[start]; // 리프 노드인 경우 자기 자신을 등록 시켜야함.
    }
    else {
        int mid = (start + end) / 2; // 아니라면 자기 밑에 자식을 더해야 함.
        return tree[node] = ((construct(a, tree, node * 2, start, mid) % mod) * (construct(a, tree, node * 2 + 1, mid + 1, end) % mod)) % mod;
    }
}

long long update(vector<long long> &tree, int node, int start, int end, int index, long long newVal){
    if (index < start || index > end) return tree[node]; // 범위 밖에 있는 경우
    if (start == end) return tree[node] = newVal;
    if (start != end){ //리프 노드가 아닌 경우
        int mid = (start + end) / 2;
        return tree[node] = (update(tree, node * 2, start, mid, index, newVal) * update(tree, node * 2 + 1, mid + 1, end, index, newVal)) % mod;
    }
}
// 내가 구하고 싶은 값의 범위 : left ~ right, 현재 체크 하고 있는 노드의 범위 : start ~ end
long long mult(vector<long long> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start) { // 만약에 포함이 안되어있는 경우
        return 1;
    }
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return (mult(tree, node * 2, start, mid, left, right) * mult(tree, node * 2 + 1, mid + 1, end, left, right)) % mod;
}

int main(){
    int n, m, k;
    scanf("%d %d %d", &n, &m, &k);
    vector<long long> a(n);
    int tree_height = (int)ceil(log2(n));
    int tree_size = (1 << (tree_height + 1));
    vector<long long> tree(tree_size);

    for (int i = 0; i < n; i++){
        scanf("%lld", &a[i]);
    }
    construct(a, tree, 1, 0, n - 1);

    int t = m + k;
    while (t--){
        int oper;
        scanf("%d", &oper);

        if (oper == 1){
            int x;
            long long y;
            scanf("%d %lld", &x, &y);
            update(tree, 1, 0, n - 1, x - 1, y);
        }//update

        if (oper == 2){
            int left, right;
            scanf("%d %d", &left, &right);
            printf("%lld\n", mult(tree, 1, 0, n - 1, left - 1, right - 1));
        }//sum
    }

    return 0;
}

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

[백준] 1365번 꼬인 전깃줄  (0) 2019.11.23
[백준] 2268번 수들의 합  (0) 2019.11.23
[백준] 1275번 커피숍2  (0) 2019.11.23
[백준] 11650번 좌표 정렬하기  (0) 2019.11.23
[백준] 2357번 최솟값과 최댓값  (0) 2019.11.23

[적용한 알고리즘]

세그먼트 트리

 

[아이디어]

내용

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

내용

 

<코드>

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;

long long construct(vector<long long> &a, vector<long long> &tree, int node, int start, int end){
    if (start == end){
        return tree[node] = a[start]; // 리프 노드인 경우 자기 자신을 등록 시켜야함.
    }
    else {
        int mid = (start + end) / 2; // 아니라면 자기 밑에 자식을 더해야 함.
        return tree[node] = construct(a, tree, node * 2, start, mid) + construct(a, tree, node * 2 + 1, mid + 1, end);
    }
}

long long update(vector<long long> &tree, int node, int start, int end, int index, long long newVal){
    if (index < start || index > end) return tree[node]; // 범위 밖에 있는 경우
    if (start == end) return tree[node] = newVal;
    if (start != end){ //리프 노드가 아닌 경우
        int mid = (start + end) / 2;
        return tree[node] = update(tree, node * 2, start, mid, index, newVal) + update(tree, node * 2 + 1, mid + 1, end, index, newVal);
    }
}
// 내가 구하고 싶은 값의 범위 : left ~ right, 현재 체크 하고 있는 노드의 범위 : start ~ end
long long sum(vector<long long> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start) { // 만약에 포함이 안되어있는 경우
        return 0;
    }
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return sum(tree, node * 2, start, mid, left, right) + sum(tree, node * 2 + 1, mid + 1, end, left, right);
}

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    vector<long long> a(n);
    int tree_height = (int)ceil(log2(n));
    int tree_size = (1 << (tree_height + 1));
    vector<long long> tree(tree_size);

    for (int i = 0; i < n; i++){
        scanf("%lld", &a[i]);
    }
    construct(a, tree, 1, 0, n - 1);

    while (m--){
        int sum_x, sum_y;
        long long change_idx, change_y;

        scanf("%d %d %lld %lld", &sum_x, &sum_y, &change_idx, &change_y);

        if (sum_x > sum_y) swap(sum_x, sum_y);
        printf("%lld\n", sum(tree, 1, 0, n - 1, sum_x - 1, sum_y - 1));

        update(tree, 1, 0, n - 1, change_idx - 1, change_y);
    }
    return 0;
}

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

[백준] 2268번 수들의 합  (0) 2019.11.23
[백준] 11505번 구간 곱 구하기  (0) 2019.11.23
[백준] 11650번 좌표 정렬하기  (0) 2019.11.23
[백준] 2357번 최솟값과 최댓값  (0) 2019.11.23
[백준] 10868번 최솟값  (0) 2019.11.23

[적용한 알고리즘]

X

 

[아이디어]

내용

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

연산자 오버로딩을 연습하기 위한 문제로..

 

<코드>

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

bool compare (pair<int, int> a, pair<int, int> b){
    if (a.first == b.first){ //첫번째 비교 대상이 같다면
        return a.second < b.second; //두번째 애를 비교해 큰 놈을 뒤로 보내준다.
    }
    else {
        return a.first < b.first;
    } // 오름차순으로 정렬해주세요
}

int main(){
    int n;
    scanf("%d", &n);
    vector<pair<int, int>> a;
    for (int i = 0; i < n; i++){
        int x, y;
        scanf("%d %d", &x, &y);
        a.push_back({x, y});
    }

    sort(a.begin(), a.end(), compare);

    for (int i = 0; i < n; i++){
        printf("%d %d\n", a[i].first, a[i].second);
    }

    return 0;
}

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

[백준] 11505번 구간 곱 구하기  (0) 2019.11.23
[백준] 1275번 커피숍2  (0) 2019.11.23
[백준] 2357번 최솟값과 최댓값  (0) 2019.11.23
[백준] 10868번 최솟값  (0) 2019.11.23
[백준] 2042번 구간 합 구하기  (0) 2019.11.23

[적용한 알고리즘]

세그먼트 트리

 

[아이디어]

내용

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

세그먼트 트리 응용을 풀러 가야겠다.

더 간단하게 풀 수 있을 거 같은데..

 

<코드>

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;

long long construct_min(vector<long long> &a, vector<long long> &tree, int node, int start, int end){
    if (start == end){
        return tree[node] = a[start];
    }
    else {
        int mid = (start + end) / 2;
        return tree[node] = min(construct_min(a, tree, node * 2, start, mid), construct_min(a, tree, node * 2 + 1, mid + 1, end));
    }
}
long long construct_max(vector<long long> &a, vector<long long> &tree, int node, int start, int end){
    if (start == end){
        return tree[node] = a[start];
    }
    else {
        int mid = (start + end) / 2;
        return tree[node] = max(construct_max(a, tree, node * 2, start, mid), construct_max(a, tree, node * 2 + 1, mid + 1, end));
    }
}

//left : 구하고 싶은 범위의 시작, right : 구하고 싶은 범위의 끝
long long SegmentTree_min(vector<long long> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start){
        return 1000000001;
    } // 범위를 벗어날 때
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return min(SegmentTree_min(tree, node * 2, start, mid, left, right), SegmentTree_min(tree, node * 2 + 1, mid + 1, end, left, right));
}

long long SegmentTree_max(vector<long long> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start){
        return -1;
    } // 범위를 벗어날 때
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return max(SegmentTree_max(tree, node * 2, start, mid, left, right), SegmentTree_max(tree, node * 2 + 1, mid + 1, end, left, right));
}

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    vector<long long> a(n);
    int h = (int) ceil(log2(n));
    int tree_size = (1 << (h + 1));
    vector<long long> tree_min(tree_size);
    vector<long long> tree_max(tree_size);

    for (int i = 0; i < n; i++){
        scanf("%lld", &a[i]);
    }
    construct_min(a, tree_min, 1, 0, n - 1);
    construct_max(a, tree_max, 1, 0, n - 1);

    while (m--){
        int x, y;
        scanf("%d %d", &x, &y);
        printf("%lld %lld\n", SegmentTree_min(tree_min, 1, 0, n - 1, x - 1, y - 1), SegmentTree_max(tree_max, 1, 0, n - 1, x - 1, y - 1));
    }

    return 0;
}

 

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

[백준] 1275번 커피숍2  (0) 2019.11.23
[백준] 11650번 좌표 정렬하기  (0) 2019.11.23
[백준] 10868번 최솟값  (0) 2019.11.23
[백준] 2042번 구간 합 구하기  (0) 2019.11.23
[백준] 16933번 벽 부수고 이동하기 3  (0) 2019.11.20

[적용한 알고리즘]

세그먼트 트리

 

[아이디어]

내용

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

세그먼트 트리에서 update 함수가 빠진 모양

그냥 응용이었다.

 

 

<코드>

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;

long long construct(vector<long long> &a, vector<long long> &tree, int node, int start, int end){
    if (start == end){
        return tree[node] = a[start];
    }
    else {
        int mid = (start + end) / 2;
        return tree[node] = min(construct(a, tree, node * 2, start, mid), construct(a, tree, node * 2 + 1, mid + 1, end));
    }
}
//left : 구하고 싶은 범위의 시작, right : 구하고 싶은 범위의 끝
long long SegmentTree(vector<long long> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start){
        return 1000000001;
    } // 범위를 벗어날 때
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return min(SegmentTree(tree, node * 2, start, mid, left, right), SegmentTree(tree, node * 2 + 1, mid + 1, end, left, right));
}

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    vector<long long> a(n);
    int h = (int) ceil(log2(n));
    int tree_size = (1 << (h + 1));
    vector<long long> tree(tree_size);

    for (int i = 0; i < n; i++){
        scanf("%lld", &a[i]);
    }
    construct(a, tree, 1, 0, n - 1);

    while (m--){
        int x, y;
        scanf("%d %d", &x, &y);
        printf("%lld\n", SegmentTree(tree, 1, 0, n - 1, x - 1, y - 1));
    }

    return 0;
}

[적용한 알고리즘]

세그먼트 트리

 

[아이디어]

기본 문제래요

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

어렵

 

<코드>

#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;

long long construct(vector<long long> &a, vector<long long> &tree, int node, int start, int end){
    if (start == end){
        return tree[node] = a[start]; // 리프 노드인 경우 자기 자신을 등록 시켜야함.
    }
    else {
        int mid = (start + end) / 2; // 아니라면 자기 밑에 자식을 더해야 함.
        return tree[node] = construct(a, tree, node * 2, start, mid) + construct(a, tree, node * 2 + 1, mid + 1, end);
    }
}

void update(vector<long long> &tree, int node, int start, int end, int index, long long delta){
    if (index < start || index > end) return; // 범위 밖에 있는 경우
    tree[node] += delta;
    if (start != end){ //리프 노드가 아닌 경우
        int mid = (start + end) / 2;
        update(tree, node * 2, start, mid, index, delta);
        update(tree, node * 2 + 1, mid + 1, end, index, delta);
    }
}
// 내가 구하고 싶은 값의 범위 : left ~ right, 현재 체크 하고 있는 노드의 범위 : start ~ end
long long sum(vector<long long> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start) { // 만약에 포함이 안되어있는 경우
        return 0;
    }
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return sum(tree, node * 2, start, mid, left, right) + sum(tree, node * 2 + 1, mid + 1, end, left, right);
}

int main(){
    int n, m, k;
    scanf("%d %d %d", &n, &m, &k);
    vector<long long> a(n);
    int tree_height = (int)ceil(log2(n));
    int tree_size = (1 << (tree_height + 1));
    vector<long long> tree(tree_size);

    for (int i = 0; i < n; i++){
        scanf("%lld", &a[i]);
    }
    construct(a, tree, 1, 0, n - 1);

    int t = m + k;
    while (t--){
        int oper;
        scanf("%d", &oper);

        if (oper == 1){
            int x;
            long long y;
            scanf("%d %lld", &x, &y);

            long long delta = y - a[x - 1];
            a[x - 1] = y;
            update(tree, 1, 0, n - 1, x - 1, delta);
        }//update

        if (oper == 2){
            int left, right;
            scanf("%d %d", &left, &right);
            printf("%lld\n", sum(tree, 1, 0, n - 1, left - 1, right - 1));
        }//sum
    }

    return 0;
}

 

(19/11/23 수정)

delta 를 이용하는게 덧셈에서는 유용한데, 곱하기 나누기에서는 참 곤란하다. 그래서 그냥 newVal 을 넘겨주는 식으로 짜면 

코드도 더 직관적이고, 범용성도 넓어지는 것 같아서 이렇게 쓰려고 한다. (밑을 참고)

#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;

long long construct(vector<long long> &a, vector<long long> &tree, int node, int start, int end){
    if (start == end){
        return tree[node] = a[start]; // 리프 노드인 경우 자기 자신을 등록 시켜야함.
    }
    else {
        int mid = (start + end) / 2; // 아니라면 자기 밑에 자식을 더해야 함.
        return tree[node] = construct(a, tree, node * 2, start, mid) + construct(a, tree, node * 2 + 1, mid + 1, end);
    }
}

long long update(vector<long long> &tree, int node, int start, int end, int index, long long newVal){
    if (index < start || index > end) return tree[node]; // 범위 밖에 있는 경우
    if (start == end) return tree[node] = newVal;
    if (start != end){ //리프 노드가 아닌 경우
        int mid = (start + end) / 2;
        return tree[node] = update(tree, node * 2, start, mid, index, newVal) + update(tree, node * 2 + 1, mid + 1, end, index, newVal);
    }
}
// 내가 구하고 싶은 값의 범위 : left ~ right, 현재 체크 하고 있는 노드의 범위 : start ~ end
long long sum(vector<long long> &tree, int node, int start, int end, int left, int right){
    if (left > end || right < start) { // 만약에 포함이 안되어있는 경우
        return 0;
    }
    if (left <= start && end <= right){
        return tree[node];
    }
    int mid = (start + end) / 2;
    return sum(tree, node * 2, start, mid, left, right) + sum(tree, node * 2 + 1, mid + 1, end, left, right);
}

int main(){
    int n, m, k;
    scanf("%d %d %d", &n, &m, &k);
    vector<long long> a(n);
    int tree_height = (int)ceil(log2(n));
    int tree_size = (1 << (tree_height + 1));
    vector<long long> tree(tree_size);

    for (int i = 0; i < n; i++){
        scanf("%lld", &a[i]);
    }
    construct(a, tree, 1, 0, n - 1);

    int t = m + k;
    while (t--){
        int oper;
        scanf("%d", &oper);

        if (oper == 1){
            int x;
            long long y;
            scanf("%d %lld", &x, &y);
            update(tree, 1, 0, n - 1, x - 1, y);
        }//update

        if (oper == 2){
            int left, right;
            scanf("%d %d", &left, &right);
            printf("%lld\n", sum(tree, 1, 0, n - 1, left - 1, right - 1));
        }//sum
    }

    return 0;
}

[적용한 알고리즘]

BFS

 

[아이디어]

낮과 밤을 토글을 이용해 하나의 정보로 또 넘겨주자.

 

[생각의 흐름 / 절차] 

뭔가 직관적으로 한거라 설명이 힘든데

사실 이동한 날의 수는 중요하지 않고, 거리만 중요하다.

낮이든 밤이든 비어있는 곳으로는 갈 수 있으니 어렵지 않다. 

 

문제는 낮에만 벽을 부술 수 있다는 것. 

현재 밤이면? -> 낮이 올 때까지 기다려야함. 

 

이걸 코드로 구현하는게 참 오래걸렸다. 

4차원 배열을 쓰는게 맞나싶긴하다. 

(어쨌든 맞았으니)

 

주석으로 보는게 나을 듯 하다.

 

[교훈]

열심히 살자

 

<코드>

#include <cstdio>
#include <algorithm>
#include <queue>
#include <tuple>
#define size 1001
using namespace std;

int n, m, k;
int dirx[] = {-1, 1, 0, 0};
int diry[] = {0, 0, -1, 1};
int map[size][size];
int visited[size][size][11][2];
queue<tuple<int, int, int, int>> q;

bool inMap(int x, int y){
    if (1 <= x && x <= n && 1 <= y && y <= m) return true;
    return false;
}

int bfs(){
    //z = 0 이면 아직 벽을 한번도 안뚫은 상태
    //z = 1 이면 벽을 한 번 뚫은 상
    while (!q.empty()){
        int curx, cury, curz, move;
        tie(curx, cury, curz, move) = q.front();
        q.pop();

        for (int i = 0; i < 4; i++){
            int nx = curx + dirx[i];
            int ny = cury + diry[i];

            if (inMap(nx, ny)){ // 다음에 방문할 곳이 map 안에 있다면
                //비어있는 곳으로 가는건 딱히 낮 밤에 구애 받지 않음.
                if (map[nx][ny] == 0 && visited[nx][ny][curz][(move + 1) % 2] == 0){
                    visited[nx][ny][curz][(move + 1) % 2] = visited[curx][cury][curz][move % 2] + 1;
                    q.push({nx, ny, curz, (move + 1) % 2});
                } // 다음 이동하는 곳에 벽이 없고, 아직 방문하지 않은 곳이라면

                // 벽을 부수고 싶은 경우
                if (move == 0){ // 현재 낮인 경우 -> 그냥 부숴버리면 됨
                    if (curz < k && map[nx][ny] == 1 && visited[nx][ny][curz + 1][(move + 1) % 2] == 0){
                        visited[nx][ny][curz + 1][(move + 1) % 2] = visited[curx][cury][curz][move % 2] + 1;
                        q.push({nx, ny, curz + 1, (move + 1) % 2});
                    }
                }
                else { // 현재 밤인 경우 -> 하루를 존버 타야함
                    // if 문에서 (move + 2) % 2 를 봐주는 이유는 (사실 move % 2 와 같지만 직관적으로 보이려고)
                    // 다음날 낮까지 존버를 탔을 때 그 다음날을 봐줘야하기 때문임.
                    if (curz < k && map[nx][ny] == 1 && visited[nx][ny][curz + 1][(move + 2) % 2] == 0){
                        visited[curx][cury][curz][(move + 1) % 2] = visited[curx][cury][curz][move % 2] + 1;
                        q.push({curx, cury, curz, (move + 1) % 2});
                    }
                }
            }
        }
    }

    int ans = 1000000000;
    bool flag = false;
    for (int i = 0; i <= k; i++){
        for (int j = 0; j < 2; j++){
            if (visited[n][m][i][j] > 0){
                flag = true;
                ans = min(ans, visited[n][m][i][j]);
            }
        }
    }
    if (flag) return ans;
    return -1;
}
int main(){
    scanf("%d %d %d", &n, &m, &k);
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
            scanf("%1d", &map[i][j]);
        }
    }

    q.push({1, 1, 0, 0});
    visited[1][1][0][0] = 1;

    printf("%d\n", bfs());
    return 0;
}

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

[백준] 10868번 최솟값  (0) 2019.11.23
[백준] 2042번 구간 합 구하기  (0) 2019.11.23
[백준] 14442번 벽 부수고 이동하기 2  (0) 2019.11.20
[백준] 2206번 벽 부수고 이동하기  (0) 2019.11.20
[백준] 12886번 돌 그룹  (0) 2019.11.20

[적용한 알고리즘]

BFS

 

[아이디어]

벽 부수고 이동하기 1과 동일

 

[생각의 흐름 / 절차] 

내용

 

[교훈]

내용

 

<코드>

#include <cstdio>
#include <algorithm>
#include <queue>
#include <tuple>
#define size 1001
using namespace std;

int n, m, k;
int dirx[] = {-1, 1, 0, 0};
int diry[] = {0, 0, -1, 1};
int map[size][size];
int visited[size][size][11];
queue<tuple<int, int, int>> q;

bool inMap(int x, int y){
    if (1 <= x && x <= n && 1 <= y && y <= m) return true;
    return false;
}

int bfs(){
    //z = 0 이면 아직 벽을 한번도 안뚫은 상태
    //z = 1 이면 벽을 한 번 뚫은 상
    while (!q.empty()){
        int curx, cury, curz;
        tie(curx, cury, curz) = q.front();
        q.pop();

        for (int i = 0; i < 4; i++){
            int nx = curx + dirx[i];
            int ny = cury + diry[i];

            if (inMap(nx, ny)){ // 다음에 방문할 곳이 map 안에 있다면
                if (map[nx][ny] == 0 && visited[nx][ny][curz] == 0){
                    visited[nx][ny][curz] = visited[curx][cury][curz] + 1;
                    q.push({nx, ny, curz});
                } // 다음 이동하는 곳에 벽이 없고, 아직 방문하지 않은 곳이라면

                if (curz < k && map[nx][ny] == 1 && visited[nx][ny][curz + 1] == 0){
                    visited[nx][ny][curz + 1] = visited[curx][cury][curz] + 1;
                    q.push({nx, ny, curz + 1});
                } // 현재 벽을 부수지 않은 상태고, 다음에 갈 곳이 벽이고, 아직 방문하지 않은 상태라면
            }
        }
    }

    int ans = 1000000000;
    bool flag = false;
    for (int i = 0; i <= k; i++){
        if (visited[n][m][i] > 0){
            flag = true;
            ans = min(ans, visited[n][m][i]);
        }
    }
    if (flag) return ans;
    return -1;
}
int main(){
    scanf("%d %d %d", &n, &m, &k);
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
            scanf("%1d", &map[i][j]);
        }
    }

    q.push({1, 1, 0});
    visited[1][1][0] = 1;

    printf("%d\n", bfs());
    return 0;
}

+ Recent posts