반응형

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

 

4256번: 트리

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 노드의 개수 n이 주어진다. (1 ≤ n ≤ 1,000) BT의 모든 노드에는 1부터 n까지 서로 다른 번호가 매겨져 있다. 다음

www.acmicpc.net

1. 문제설명

트리의 전위순회 결과와 중위순회 결과를 바탕으로 트리의 구조를 구해낼 수 있고,

이를 후위순회한 결과를 출력하는 문제이다.

 

전위 순회한 결과 먼저오는 노드가 루트노드임을 알 수 있고,

그 루트노드를 바탕으로 중위순회 결과를 보면

해당 루트노드의 왼쪽 서브트리와 오른쪽 서브트리를 알 수 있다.

 

이를 재귀적으로 구현하면 트리 구조를 구현할 수 있고,

이를 후위순회하면  정답이 된다.

 

 

 

 

 

2. 문제풀이코드

#include <bits/stdc++.h>
using namespace std;

int n,pidx, preOrderRes[1001], inOrderRes[1001], order[1001];

struct Node{
    int data;
    Node* left = nullptr;
    Node* right = nullptr;
    Node(int _data, Node* _left, Node* _right): data(_data), left(_left), right(_right){}
};


void postOrder(Node* root){
    if(root== nullptr) return;
    postOrder(root->left);
    postOrder(root->right);
    cout << root->data << ' ';
}

void deconstruct(Node* root){
    if(root == nullptr) return;
    deconstruct(root->left);
    deconstruct(root->right);
    root = nullptr;
    delete root;
}


Node* makeTree(int start, int end){
    if(start > end) return nullptr;
    
    Node* newNode = new Node(preOrderRes[pidx++], nullptr, nullptr);
    if(start==end) return newNode;
    int mid = order[newNode->data];
    newNode->left = makeTree(start, mid-1);
    newNode->right = makeTree(mid+1, end);
    return newNode;
}

void makeTree(Node** root){
    pidx = 1;
    *root = makeTree(1, n);
}



int main() {
    ios::sync_with_stdio(0); cin.tie(0);

    int t;
    cin >> t;
    for(int T=0; T<t; T++){
        cin >> n;
        for(int i=1; i<=n; i++){
            cin >> preOrderRes[i];
        }
        for(int i=1; i<=n; i++){
            cin >> inOrderRes[i];
            order[inOrderRes[i]] = i;
        }
        Node *root = nullptr;

        makeTree(&root);
        postOrder(root);
        cout << '\n';
        deconstruct(root);
    }

    return 0;
}
반응형
반응형

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

 

21611번: 마법사 상어와 블리자드

마법사 상어는 파이어볼, 토네이도, 파이어스톰, 물복사버그, 비바라기 마법을 할 수 있다. 오늘 새로 배운 마법은 블리자드이고, 크기가 N×N인 격자에서 연습하려고 한다. N은 항상 홀수이고, (

www.acmicpc.net

1.문제설명

해당 문제에서 거쳐야하는 과정을 크게 4가지로 분리할 수 있다.

 

1. 블리자드해서 구슬을 삭제한다.

2. 구슬의 빈자리를 메꾸도록 모든 구슬을 앞으로 이동시킨다.

3. 구슬폭발 : 이동후 같은 구슬이 4개 이상 모여있는 구슬들을 삭제한다. (삭제후 2번과 동일하게 앞으로 이동)

4. 구슬변화 : 남아있는 구슬을 확인하면서, 연속된 구슬의 개수와 해당 구슬의 숫자로 구슬을 변화시킨다.

 

 

이 문제를 풀기 위한 배열과 연결리스트 중 어떤 자료구조를 선택할 지 고민했는데,

구슬폭발로 인해 원소를 자주 삭제해야하니까 연결리스트가 시간복잡도 상 유리하지 않을까 생각했다.

 

그런데 좀 더 생각해보니 구슬이 폭발한다고 원소를 바로 바로 삭제해서 푸는게 아니라

폭발하기 전 배열에서 폭발할 구슬들을 확인한 후 제외하는 방식으로 풀어야하기 때문에

인덱스 접근을 해야할 일이 많아서 배열로 푸는 것으로 결정했다.

 

 

의문이 들었던 것 중 하나는

4. 구슬변화 후 다시 구슬폭발이 일어나는 경우가 있지 않을 까 의심했는데,

깊이 생각해보니 구슬폭발이 모두 끝난 후에는 구슬 변화로 인해 연속적인 구슬 4개가 생길 수 없다.

예를 들어서 2가 2개 있는 경우, 또 2가 2개있는 경우 이러면 2222가 되니까 또 폭발할 수 있지 않을 까 생각했는데

애초에 구슬폭발이 모두 끝난 경우에는 2가 2개, 2가 2개 이런 경우가 나타날 수 없다.

다른 경우도 마찬가지이다.

 

 

문제에서 2차원 배열 형식으로 그림을 제공했지만

이를 1차원 배열로 전환한 후 문제를 해결해야한다.

반복문을 통해서 2차원배열의 1번부터 N*N-1번까지 돌 수 있다.

 

블리자드를 구현할 때는 1차원 배열내에서 가능한데,

계차수열을 이용해서 1차원 배열에서 반복문을 돌면서 배열의 원소를 지워나가면 해결할 수 있다.

 

2.문제풀이코드 C++

#include <bits/stdc++.h>

using namespace std;

int N, M, originArr[51][51], marbles[2501], ans[4];

void originToMarbles() {
    int x = (N + 1) / 2;
    int y = x;

    int dx[4] = {0, 1, 0, -1};
    int dy[4] = {-1, 0, 1, 0};

    int curDir = 0;
    int moveCnt = 1;
    int turnDirCnt = 0;

    for (int i = 0; i < N * N; i++) {
        marbles[i] = originArr[x][y];
        turnDirCnt++;

        x += dx[curDir];
        y += dy[curDir];

        if (turnDirCnt == moveCnt) {
            if (curDir == 1 || curDir == 3) moveCnt++;
            curDir = (curDir + 1) % 4;
            turnDirCnt = 0;
        }
    }
}

void blizzard(int d, int s) {
    int firstNum[5] = {0, 7, 3, 1, 5};
    int firstStep[5] = {0, 15, 11, 9, 13};

    int num = firstNum[d];
    int step = firstStep[d];
    for (int i = 1; i <= s; i++) {
        marbles[num] = 0;
        num += step;
        step += 8;
    }
}

void marbleMove() {
    int tmp[2501];
    memset(tmp, 0, sizeof(tmp));
    int tmpIdx = 1;

    for (int i = 1; i < N * N; i++) {
        if (marbles[i] == 0) continue;
        tmp[tmpIdx++] = marbles[i];
    }
    swap(marbles, tmp);
}

bool marbleExplode() {
    bool flag = false;
    int sameFirst = 1;
    int sameCnt = 1;
    for (int i = 2; i <= N * N; i++) {
        if (marbles[i] != 0 && marbles[i] == marbles[i - 1]) {
            sameCnt++;
        } else {
            if (sameCnt >= 4) {
                ans[marbles[i-1]] += i - sameFirst;
                for (int j = sameFirst; j <= i - 1; j++) {
                    marbles[j] = 0;
                }
                flag = true;
            }
            sameFirst = i;
            sameCnt = 1;
        }
    }
    return flag;
}

void marbleChange() {
    int tmp[2501];
    memset(tmp, 0 ,sizeof(tmp));
    int tmpIdx = 1;

    int sameCnt = 1;
    for(int i=2; i<=N*N; i++){
        if(marbles[i]==marbles[i-1]){
            sameCnt++;
        }
        else{
            tmp[tmpIdx++] = sameCnt;
            if(tmpIdx == N*N) break;
            tmp[tmpIdx++] = marbles[i-1];
            if(tmpIdx == N*N) break;
            sameCnt = 1;
        }
    }
    swap(tmp, marbles);
}


int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> N >> M;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            cin >> originArr[i][j];
        }
    }

    originToMarbles();


    for (int i = 1; i <= M; i++) {
        int d, s;
        cin >> d >> s;
        blizzard(d, s);
        marbleMove();
        while (marbleExplode()) {
            marbleMove();
        }
        marbleChange();
    }
    
    cout << ans[1] + 2 * ans[2] + 3 * ans[3] << '\n';
    return 0;
}

 

반응형
반응형

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

 

20061번: 모노미노도미노 2

모노미노도미노는 아래와 같이 생긴 보드에서 진행되는 게임이다. 보드는 빨간색 보드, 파란색 보드, 초록색 보드가 그림과 같이 붙어있는 형태이다. 게임에서 사용하는 좌표 (x, y)에서 x는 행,

www.acmicpc.net

1. 문제설명

구조 설계를 꼼꼼히 잘 해야 해결할 수 있는 문제이다.

먼저 파란색 블록과 초록색 블록 영역은 동일하게 둘 수 있다.

들어오는 블록이 1x2거나 2x1일 때 그 블록을 서로 바꿔서 파란 영역과 초록 영역에 넣어주면 동일한 구조를 지닌다.

 

즉 input 블록만 회전시켜주면 초록색 영역과 파란색 영역에 동일한 시행을 하면 된다.

이를 바탕으로

나는 함수를 세가지를 만들었는데

 

1. 블록을 쌓는 함수,

2. 현재 쌓인 블록에서 지워질 라인이 있는지 확인하고 지우며 점수를 증가시키는 함수

3. 특별한 영역에 블록이 있는지 확인하고 그에 따라 전체 블록을 옮기는 함수.

 

이 세 함수를 들어오는 블록마다 파란영역과 초록 영역에 시행하면

문제를 해결할 수 있다.

 

주의해야할 점은

2. 현재 쌓인 블록에서 지워질 라인이 있는지 확인하고 지운 후

다시 현재 라인부터 검사해야한다.

왜냐하면 현재 라인은 이후에 검사할 라인에서 당겨왔기 때문에

검사가 안된 상태이기 때문이다.

 

 

이러한 삼성기출문제의 구현, 시뮬레이션 문제는 

생각을 구조화하고 짜임새있게 해주어서 좋은 문제인 것 같다.

 

 

2.문제풀이코드 C++

#include <bits/stdc++.h>

using namespace std;

int N, point;
bool blueBoard[4][6], greenBoard[4][6];


void buildBlock(int t, int x, int y, bool board[][6]) {
    if(t==1){
        int ny = 0;
        while(ny+1 <= 5 && board[x][ny+1] ==0 ){
            ny++;
        }
        board[x][ny] = 1;
    }
    else if(t==2){
        int ny = 0;
        while(ny+1 <= 5 && board[x][ny+1] ==0 ){
            ny++;
        }
        board[x][ny] = 1;
        board[x][ny-1] = 1;

    }
    else if(t==3){
        int nyFirst = 0;
        while(nyFirst+1 <= 5 && board[x][nyFirst+1] ==0 ){
            nyFirst++;
        }

        int nySecond = 0;
        while(nySecond+1 <= 5 && board[x+1][nySecond+1] ==0 ){
            nySecond++;
        }

        int ny = min(nyFirst, nySecond);

        board[x][ny] = 1;
        board[x+1][ny] = 1;
    }
}

void checkNormalLine(bool blueBoard[][6]) {
    for(int i=5; i>=2; i--){
        bool flag = true;

        for(int j=0; j<4; j++){
            if(!blueBoard[j][i]){
                flag = false;
                break;
            }
        }

        if(flag){
            //Line delete;
            point++;
            for(int j=i; j>=1; j--){
                for(int k=0; k<4; k++){
                    blueBoard[k][j] = blueBoard[k][j-1];
                }
            }

            for(int k=0; k<4; k++){
                blueBoard[k][0] = 0;
            }
            //cur Line recheck!
            i++;
        }
    }
}

void checkSpecialLine(bool blueBoard[][6]) {
    bool flag0 = false;
    bool flag1 = false;
    for(int i=0; i<4; i++){
        if(blueBoard[i][0]){
            flag0 = true;
        }
        if(blueBoard[i][1]){
            flag1 = true;
        }
    }


    if(flag0&&flag1){
        //2칸
        for(int i=5; i>=2; i--){
            for(int j=0; j<4; j++){
                blueBoard[j][i] = blueBoard[j][i-2];
            }
        }
        for(int k=0; k<4; k++){
            blueBoard[k][0] = 0;
            blueBoard[k][1] = 0;
        }


    }
    else if(flag1){ //1칸
        for(int i=5; i>=1; i--){
            for(int j=0; j<4; j++){
                blueBoard[j][i] = blueBoard[j][i-1];
            }
        }
        for(int k=0; k<4; k++){
            blueBoard[k][0] = 0;
        }

    }

}


int getBlockNum() {
    int blocks = 0;

    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 6; j++) {
            if (blueBoard[i][j]) blocks++;
            if (greenBoard[i][j]) blocks++;
        }
    }



    return blocks;
}


void print(){
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 6; j++) {
            cout << blueBoard[i][j] << ' ';
        }
        cout << '\n';
    }

    cout << "-----------------------\n";

    for(int i=0; i<6; i++){
        for(int j=0; j<4; j++){
            cout << greenBoard[j][i] << ' ';
        }
        cout << '\n';
    }


    cout <<'\n';
    cout << "-----------------------\n";
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> N;

    for (int i = 0; i < N; i++) {
        int t, x, y;
        cin >> t >> x >> y;
        buildBlock(t, x, y, blueBoard);
        //90' 회전
        if (t == 2) t = 3;
        else if (t == 3) t = 2;
        buildBlock(t, y, x, greenBoard);

        checkNormalLine(blueBoard);
        checkNormalLine(greenBoard);
        checkSpecialLine(blueBoard);
        checkSpecialLine(greenBoard);
//        print();
    }


    cout << point << '\n';
    cout << getBlockNum() << '\n';

}
반응형
반응형

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

 

17822번: 원판 돌리기

반지름이 1, 2, ..., N인 원판이 크기가 작아지는 순으로 바닥에 놓여있고, 원판의 중심은 모두 같다. 원판의 반지름이 i이면, 그 원판을 i번째 원판이라고 한다. 각각의 원판에는 M개의 정수가 적혀

www.acmicpc.net

1.문제설명

1. 회전하는 함수를 구현한다.

2. 각 원판을 회전 후 인접점을 검사하여 같은 값을 가진 좌표가 있을 경우 해당 지점들은 모두 없앤다.

3. 인접점이 없을 경우 평균을 구해 평균보다 작은 값은 +1 평균보다 큰 값은 모두 -1을 해준다.

 

 

 

1. 회전하는 함수는 deque를 이용하면 쉽게 구현할 수 있다.

2. 인접지점을 검사하는 기능을 구현하는 것이 이 문제의 핵심인데,

규칙을 잘 발견하면 깔끔하게 구할 수 있다.

 

인접지점 중 현재 점과 같은 값을 가진 지점이 존재하는지 검사를 해야하는데

이게 현재 지점이 어떤 점위에 있느냐에 따라 케이스가 나뉘어져

복잡한 if문을 사용해서 풀 수도 있다.

 

그런데 현재 원판이 1번, 2번, ... N번 원판 까지 있으면

가상의 0번 원판, N+1번 원판이 존재한다고 생각하고

해당 원판에 존재하는 수를 모두 0으로 두면

조건을 좀 더 간단하게 만들 수 있다.

 

이런식으로 문제를 풀 수 있는 이유는

문제 조건에서 원판 위의 숫자는 자연수로만 두기 때문에

0과 비교하면 무조건 다른 값을 가진 것으로 나타난다.

 

빨간색 0이 임의로 추가해준 지점이고

파란색 동그라미의 2와 3은 해당 지점에서 인접 지점을 검사할 때

검사할 지점들을 나타낸다

 

이런식으로 구현하면 1번 원판과 N번 원판을 굳이 따로 케이스를 분류하지 않고 풀어도 된다.

 

 


2.문제풀이코드 C++

#include <bits/stdc++.h>

using namespace std;

int N, M, T;
int arr[52][52];

int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};

void Input();
double getMeans();
void plusMinus();


//row를 k번 돌린다
void rotateClock(int row, int k) {
    deque<int> dq;

    for (int i = 1; i <= M; i++) {
        dq.push_back(arr[row][i]);
    }

    for (int i = 0; i < k; i++) {
        dq.push_front(dq.back());
        dq.pop_back();
    }

    for (int i = 1; i <= M; i++) {
        arr[row][i] = dq.front();
        dq.pop_front();
    }
}

void rotate(int x, int d, int k) {
    for (int i = 1; i <= N; i++) {
        if (i % x == 0) {
            if (d == 0) {
                rotateClock(i, k);
            } else {
                rotateClock(i, M - k);
            }
        }
    }
}

void getAdjacentPoint() {
    vector<pair<int, int> > points;

    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            if (arr[i][j] == 0) continue;
            for (int k = 0; k < 4; k++) {
                int nx = i + dx[k];
                int ny = j + dy[k];

                if (ny > M) ny = 1;
                else if (ny < 1) ny = M;

                if (arr[i][j] == arr[nx][ny]) {
                    points.push_back({i, j});
                    points.push_back({nx, ny});
                }
            }
        }
    }

    if (points.size() > 0) {
        for (auto p: points) {
            arr[p.first][p.second] = 0;
        }
    } else {
        plusMinus();
    }
}


double getMeans() {
    int cnt = 0;
    double sum = 0;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            if (arr[i][j] > 0) cnt++;
            sum += arr[i][j];
        }
    }

    if (cnt == 0) return 0;
    else return sum / cnt;
}

void plusMinus() {
    double standard = getMeans();

    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            if (arr[i][j] == 0) continue;

            if (arr[i][j] > standard) {
                arr[i][j]--;
            } else if (arr[i][j] < standard) {
                arr[i][j]++;
            }
        }
    }
}


int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    Input();

    int ans = 0;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            ans += arr[i][j];
        }
    }

    cout << ans << '\n';
}


void Input() {
    cin >> N >> M >> T;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            cin >> arr[i][j];
        }
    }

    for (int t = 0; t < T; t++) {
        int x, d, k;
        cin >> x >> d >> k;

        rotate(x, d, k);
        getAdjacentPoint();
    }
}
반응형
반응형

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

 

1963번: 소수 경로

소수를 유난히도 좋아하는 창영이는 게임 아이디 비밀번호를 4자리 ‘소수’로 정해놓았다. 어느 날 창영이는 친한 친구와 대화를 나누었는데: “이제 슬슬 비번 바꿀 때도 됐잖아” “응 지금

www.acmicpc.net

1.문제설명

에라토스테네스 체를 이용하여 미리 소수를 구분할 수 있는 배열을 만들어두고

입력받은 두 소수 A와 B에서

 

A를 시작점으로 하여 자리수를 하나씩 바꿔가면서 소수인지 판별하고 BFS를 돌면서

B로 도착할 수 있는지 확인하는 문제

 

 

 


 

다음 숫자를 구하는 식을 꼼꼼하게 잘 세워야 한다.

 

8033

1. 1000의 자리숫자 바꿔가면서 확인

0033 1033 2033 3033 4033 5033 6033 7033 8033 9033

 

2. 100의 자리숫자 바꿔가면서 확인

8033 8133 8233 ... 8933

 

3. 10의 자리 숫자 바꿔가면서 확인

8003 8013 8023 8033 ... 8093

 

4. 1의자리 숫자 바꿔가면서 확인

8030, 8031, 8032, ... 8039

 

 

 

즉 8033이 시작 숫자면

위의 모든 숫자를 한번 씩 보면서

소수인지 확인하고, 1000이상인지 확인하고, 이전에 방문했던 숫자인지 확인하여

BFS를 돌면 된다.

 

 


 

 

2.문제풀이코드 C++

#include <bits/stdc++.h>
using namespace std;

bool Prime[10000];


void eratos(){
    fill(Prime, Prime+10000, 1);

    Prime[0] = 0; Prime[1] = 0;
    int sqrtn = int(sqrt(10000));

    for(int i=2; i<=sqrtn; i++){
        for(int j=i*i; j<10000; j+=i){
            Prime[j] = 0;
        }
    }
}


int BFS(int a, int b){
    if(a==b) return 0;

    int vis[10000];
    memset(vis, 0 , sizeof(vis));

    queue<int> Q;
    Q.push(a); vis[a] = 1;


    while(!Q.empty()){
        int cur = Q.front(); Q.pop();
        if(cur==b){
            return vis[cur]-1;
        }

        int minus = 1000;
        for(int i=0; i<4; i++){
            //숫자 계산 확인하기
            int minusedNum = cur - ((cur%(minus*10))/minus)*minus;

            for(int j=0; j<=9; j++){
                int nextNum = minusedNum + j*minus;

                if(Prime[nextNum] && vis[nextNum]==0 && nextNum>=1000){
                    vis[nextNum] = vis[cur] + 1;
                    Q.push(nextNum);
                }
            }
            minus/=10;
        }
    }
    return -1;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int t;
    cin >> t;


    eratos();


    for(int T=0;T<t; T++){
        int a, b;
        cin >> a >> b;

        int tmp = BFS(a, b);

        if(tmp<0){
            cout <<"Impossible\n";
        }
        else{
            cout << tmp << '\n';
        }
    }

    return 0;
}

백준 1963번 소수경로

반응형
반응형

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

 

1600번: 말이 되고픈 원숭이

첫째 줄에 정수 K가 주어진다. 둘째 줄에 격자판의 가로길이 W, 세로길이 H가 주어진다. 그 다음 H줄에 걸쳐 W개의 숫자가 주어지는데, 0은 아무것도 없는 평지, 1은 장애물을 뜻한다. 장애물이 있

www.acmicpc.net

1. 문제설명

일반적인 BFS문제는 상하좌우로 네칸만 움직인다.

이 문제는 원숭이가 평소에는 상하좌우로 네칸을 움직일 수 있다가

K번의 기회로 말처럼 더 넓은 범위를 움직일 수 있다.

 

이런 문제를 표현하기 위해서는 최대 K번 말처럼 움직일 수 있으므로

현재까지 원숭이가 총 몇 번 말처럼 움직였는지 상태를 저장해줄 필요가 있다.

그래서 이러한 문제 유형을 나는 상태 추가 BFS라고 부르기로 했다.

그냥 저 혼자 부르는 거니까 다른데 가서 유식한 척 말하시면 안됩니다.

 

 

 

2. 상태추가?

왜 상태추가라고 하냐면

보통 일반적으로 BFS를 돌 때는

x,y 좌표를 방문했는지 여부를 저장하면서 중복된 행동을 하지 않도록 합니다.

 

이 문제도 단지 x,y좌표를 방문했는지 여부와 동일하게

x,y좌표 방문 여부 + '말처럼 움직인 횟수' 라는 상태가 추가되어

중복된 행동을 하지 않으면서 BFS를 돈다고 생각하면 편합니다.

 

 

그리고 또한 이 문제를 왜 BFS로 해결 할 수 있냐면

원하는 답이 원숭이가 총 몇번 움직이는 행동을 했는 지 구하는 것이기 때문에

말처럼 움직이든 원숭이처럼 움직이든 특정 행동엔 관심없고

움직임 단계별로 일정하게 증가시키면서 답을 구하면 되기 때문에

너비우선탐색으로 해결 할 수 있습니다.

 

 

 


 

 

3.문제풀이코드 C++

 

#include <bits/stdc++.h>

using namespace std;

struct Position {
    int x, y, k, d;

    Position(int a, int b, int c, int dd) {
        x = a, y = b, k = c, d = dd;
    }
};

int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
//horse
int hdx[8] = {-2, -2, -1, -1, 1, 1, 2, 2};
int hdy[8] = {-1, 1, -2, 2, -2, 2, -1, 1};

queue<Position> Q;

int K, W, H;
int arr[203][203];
bool vis[203][203][32];


int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> K >> W >> H;

    // 행H , 열W
    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            cin >> arr[i][j];
        }
    }

    Q.push({0, 0, 0, 0});

    while (!Q.empty()) {
        auto cur = Q.front();
        Q.pop();

        if (cur.x == H - 1 && cur.y == W - 1) {
            cout << cur.d << '\n';
            return 0;
        }

        for (int i = 0; i < 4; i++) {
            int nx = cur.x + dx[i];
            int ny = cur.y + dy[i];

            if (nx < 0 || nx >= H || ny < 0 || ny >= W) continue;
            if (vis[nx][ny][cur.k] || arr[nx][ny] == 1) continue;

            vis[nx][ny][cur.k] = 1;
            Q.push({nx, ny, cur.k, cur.d + 1});
        }

        if (cur.k + 1 <= K) {
            for (int i = 0; i < 8; i++) {
                int nx = cur.x + hdx[i];
                int ny = cur.y + hdy[i];

                if (nx < 0 || nx >= H || ny < 0 || ny >= W) continue;
                if (vis[nx][ny][cur.k + 1] || arr[nx][ny] == 1) continue;

                vis[nx][ny][cur.k + 1] = 1;
                Q.push({nx, ny, cur.k + 1, cur.d + 1});
            }
        }
    }

    cout << -1 << '\n';


    return 0;
}

백준 1600번

반응형
반응형

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

 

16563번: 어려운 소인수분해

첫째 줄에는 자연수의 개수 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 자연수 ki (2 ≤ ki ≤ 5,000,000, 1 ≤ i ≤ N)가 N개 주어진다.

www.acmicpc.net

1.문제설명

에라토스테네스의 체를 이용하여 소인수분해를 해야하는 문제이다.

 

빠른 소인수분해를 위해 에라토스테네스의 체를 이용해서

단순히 소수를 판별하는 것을 넘어서

Prime[x] = y , x의 소인수 중 가장 작은 소수 y값을 저장하면

이를 바탕으로 소인수분해를 빠르게 할 수 있다.

 

 

예를 들어 10을 소인수 분해 할 때

Prime 배열은 다음과 같다

1 2 3 4 5 6 7 8 9 10
-1 2 3 2 5 2 7 2 3 2

int x = 10 이라고 하면

Prime[x] == 2 이므로

2를 출력하고 x = x/2를 해준다

그러면 Prime[x] =5 이므로

5를 출력하고 x = x/5

 

x==1 이므로 종료한다.

 

이런식으로 에라토스테네스체를 이용하여 빠른 소인수분해를 할 수 있다.

 

 


 

2.문제풀이코드

#include <bits/stdc++.h>
using namespace std;
#define LEN 5000001


//Fast Factorization

int Prime[LEN];

//O(nloglogn)
void erathostenes(){
    Prime[0] = Prime[1] = -1;

    for(int i=2; i<LEN; i++){
        Prime[i] = i;
    }
    int sqrtn = int(sqrt(LEN));

    for(int i=2; i<=sqrtn; i++){
        for(int j=i*i; j<LEN; j+=i){
            if(Prime[j] == j){
                Prime[j] = i;
            }
        }
    }
}

//아마도 O(n) 이하
void Factorization(int num){

    while(num > 1){
        cout << Prime[num] << ' ';
        num /= Prime[num];
    }

    cout << '\n';
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0);

    int t;
    cin >> t;

    erathostenes();

    for(int T=0;T<t; T++){
        int num;
        cin >> num;
        Factorization(num);
    }


    return 0;
}

백준 16563번 어려운 소인수 분해

반응형
반응형

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

 

2470번: 두 용액

첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 주어진다. 이 수들은 모두 -1,000,000,000 이상 1,000,00

www.acmicpc.net

1. 문제설명

두 용액의 합이 가장 0에 가까울 때의 두 용액의 값을 출력하는 문제이다.

 

단순하게 나올 수 있는 모든 용액의 합을 구하면 시간복잡도가 O(N^2)이 되기 때문에 틀린다.

 

우선 모든 용액의 값을 받아 정렬하고

가장 작은값과 가장 큰값을 start 지점과 end지점으로 두어서 더해본다.

 

v[start] + v[end] > 0 이라면 두 용액의 합이 0에 가까워지기 위해서

두용액의 합을 줄여야한다.

 

이 때 이미 모든 용액은 오름차순으로 정렬되어 있기 때문에 두 용액의 합을 줄이기 위해서는

end의 값을 줄여야한다

 

반대로 v[start] + v[end] < 0 일 때는 두 용액의 합이 0에 가까워지기 위해

두 용액의 합을 키워야하므로

start의 값을 키워야한다.

 

이 과정을 start < end 인 동안 반복한다.

두 용액은 다른 용액을 사용해야 하기 때문에 start==end가 되어서는 안된다.

 

이 과정을 반복하면 abs(v[start] + v[end])가 최소가 되는 지점을 찾을 수 있다.

이를 코드로 옮기면 된다.

 

 

정렬할 때 시간복잡도 O(NlogN)

start, end를 찾는 과정 O(N) 이므로

O(NlogN)으로 이 문제를 해결 할 수 있다.

 

 


2.문제풀이코드

#include <bits/stdc++.h>
using namespace std;

int n;
vector<int> v;

int main(){
    ios::sync_with_stdio(0); cin.tie(0);

    cin >> n;
    for(int i=0; i<n; i++){
        int num;
        cin >> num;
        v.push_back(num);
    }

    sort(v.begin(), v.end());


    int st = 0, en = n-1;

    int ans_val = INT_MAX;

    int ans1, ans2;
    while(st < en){
        int sum = v[st] + v[en];
        if(sum==0) {
            cout << v[st] << ' ' << v[en];
            return 0;
        }

        if(ans_val >= abs(sum)){
            ans_val = abs(sum);
            ans1 = v[st];
            ans2 = v[en];
        }

        //두합이 0보다 크다면 en의 값을 줄여야하고 0보다 작다면 st의 값을 증가시킨다.
        if(sum > 0){
            en--;
        }
        else{
            st++;
        }
    }

    cout << ans1 << ' ' << ans2;


    return 0;
}

백준 2470번

 

반응형

+ Recent posts