반응형

문제 링크

 

16236번: 아기 상어

N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가

www.acmicpc.net

 

풀이 ) BFS

  현재 아기상어 위치를 시작으로 BFS로 먹이 후보를 찾고

  후보 중 조건에 만족하는 먹이로 이동하기를 이동할 수 없을 때까지 반복한다.

 

코드 )

import java.io.*;
import java.util.*;

class Main {

    static int n;
    static int[][] board;
    static boolean[][] visited;
    static Fish shark;
    static Fish next;
    static Queue<Fish> q;

    static int answer;

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

    public static void main(String[] args) throws IOException {
        input();	// 입력
        solve();	// 풀이
    }

    static void solve() {

        while(true) {
            int distance = init();	// 현재 상어 위치부터 다음 먹이까지의 거리 (초기화)

            while(!q.isEmpty() && next == null) {	// 다음 물고기가 없을때까지
                int size = q.size();
                distance++;
                while (size-- > 0) {
                    Fish cur = q.poll();
                    /*
                     * 다음 물고기의 크기가 더 작은 지 확인하고
                     * 더 작으면 먹을 수 있는 다른 물고기와 위치를 비교하여 갱신
                     */
                    promise(cur.x, cur.y);

                    for (int dir = 0; dir < 4; ++dir) {
                    	// 다음 위치로 이동할 수 있으면 추가
                        findRoute(cur.x+dx[dir], cur.y+dy[dir]);
                    }
                }
            }

            if(next != null) {
            	// 이동 가능한 경우
                move(distance);
            } else {
            	// 이동 불가능한 경우
                System.out.println(answer);
                break;
            }
        }
    }

    static void move(int distance) {
        answer += distance;
        // 물고기를 먹고
        board[next.x][next.y] = 0;
        shark = next;
        // 크기가 증가할 수 있으면 증가
        shark.grow();
    }

    static void findRoute(int x, int y) {
        if (!isIn(x, y) || visited[x][y] || board[x][y] > shark.size){
            return;
        }

        visited[x][y] = true;
        q.offer(new Fish(x, y, board[x][y], -1));
    }

    static void promise(int x, int y) {
        if (board[x][y] > 0 && board[x][y] < shark.size) {
            Fish candidate = new Fish(x, y, shark.size, shark.feed+1);
            if(next == null ||
                    candidate.x < next.x ||
                    (candidate.x == next.x && candidate.y < next.y)) {
                next = candidate;
            }
        }
    }

    static int init() {
        visited = new boolean[n][n];
        q = new LinkedList<>();
        q.offer(shark);
        visited[shark.x][shark.y] = true;
        next = null;
        return -1;
    }

    static boolean isIn(int nx, int ny) {
        return nx>=0&&nx<n&&ny>=0&&ny<n;
    }

    static void input() throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        board = new int[n][n];
        for(int i = 0; i < n; ++i) {
            StringTokenizer st = new StringTokenizer(br.readLine(), " ");
            for(int j = 0; j < n; ++j) {
                board[i][j] = Integer.parseInt(st.nextToken());
                if(board[i][j] == 9) {
                    shark = new Fish(i, j, 2, 0);
                    board[i][j] = 0;
                }
            }
        }
    }

    static class Fish {
        int x, y;
        int size;
        int feed;

        public Fish(int x, int y, int size, int feed) {
            this.x = x;
            this.y = y;
            this.size = size;
            this.feed = feed;
        }

        void grow() {
            if(feed == size) {
                size++;
                feed = 0;
            }
        }
    }
}

 

주의 )

  단순히 상, 좌, 우, 하 순으로 검사하게되면 4번 예시 케이스에서 답인 60이 아닌 56이 출력된다.

반응형
반응형

문제 링크

 

17142번: 연구소 3

인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 바이러스는 활성 상태와 비활성 상태가 있다. 가장 처음에 모든 바이러스는 비활성 상태이고

www.acmicpc.net

 

 

풀이 )

  처음에는 방문 여부를 나타내는 boolean 2차원 배열 visited를 선언하지 않고 풀려다가 많은 문제가 발생했다.

  • 비활성화된 바이러스를 0으로 갱신하고 시작
    • 처음에 다 퍼져있는 경우에 대해서 다른 결과를 출력한다.
  • 바이러스가 방문한 곳을 2로 갱신하고, 1이 아닌 곳만 방문
    • 큐에 중복 방문으로 인한 메모리 초과
  • 바이러스가 방문한 곳을 3으로 갱신하고, 0 2인 곳만 방문
    • 비활성화된 바이러스를 벽으로 생각함

 

  결국 visited 배열을 사용해서 했다.

 

  자세한 풀이는 아래 주석을 달아두었다.

import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[] dx = {0, 1, 0, -1};
    static int[] dy = {1, 0, -1, 0};

    static int n,m;
    static int[][] board;
    static int ans = (int)1e7;
    static ArrayList<Point> virusList = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        board = new int[n][n];
        for(int i = 0; i < n; ++i){
            st = new StringTokenizer(br.readLine(), " ");
            for(int j = 0; j < n; ++j){
                board[i][j] = Integer.parseInt(st.nextToken());
                if(board[i][j] == 2){
                    virusList.add(new Point(i, j));
                }
            }
        }
        // 입력 & 비활성화된 바이러스를 list에 저장

        combination(m, new int[m], 0);	// 활성화될 바이러스 선택(조합 - virusList.size() C m)
        if(ans == (int)1e7)
            ans = -1;
        System.out.println(ans);
    }

    static void combination(int toPick, int[] picked, int pos){
        if(toPick == 0){
            bfs(picked);	// 활성화될 바이러스를 시작으로 bfs 탐색
            return;
        }

        for(int i = pos; i < virusList.size(); ++i){
            picked[picked.length-toPick] = i;
            combination(toPick-1, picked, i+1);
        }
    }

    static void bfs(int[] picked){
        int[][] tmp = new int[n][];
        for(int i = 0; i < n; ++i)
            tmp[i] = board[i].clone();	// board 값에 영향을 주지 않기 위한 임시 배열
        boolean[][] visited = new boolean[n][n];	// 방문 여부

        Queue<Point> virusQ = new LinkedList<>();
        for(int i = 0; i < picked.length; ++i){
            Point virus = virusList.get(picked[i]);
            virusQ.offer(virus);
            visited[virus.x][virus.y] = true;
        }	// 활성화될 바이러스를 큐에 집어 넣으면서 방문여부 갱신

        int time = -1;	// 바이러스가 퍼지는데 걸리는 시간
        while(!virusQ.isEmpty()){
            time++;
            boolean flag = false;	// 바이러스가 퍼졌는지 확인할 때 2중 for문을 탈출하기 위한 flag
            for(int i = 0; i < n; ++i){
                for(int j = 0; j < n; ++j)
                    if(tmp[i][j] == 0){
                        flag = true;
                        break;
                    }
                if(flag)
                    break;
            }
            if(!flag){	// 바이러스가 더이상 퍼질 수 없으면 ans를 갱신하고 종료
                ans = Math.min(ans, time);
                return;
            }

            int size = virusQ.size();
            while(size-- > 0){
                Point cur = virusQ.poll();
                int x = cur.x, y = cur.y;
                for(int dir = 0; dir < 4; ++dir){
                    int nx = x+dx[dir], ny = y+dy[dir];
                    if(!isIn(nx, ny) || tmp[nx][ny] == 1 || visited[nx][ny])	// 확산할 수 없는 경우
                        continue;
                    visited[nx][ny] = true;	// 확산할 수 있는 경우 방문 여부와 임시 배열을 갱신하고, 큐에 추가
                    tmp[nx][ny] = 2;
                    virusQ.offer(new Point(nx, ny));
                }
            }
            if(virusQ.isEmpty())
                break;
        }
    }

    static boolean isIn(int x, int y){
        return 0 <= x && x < n && 0 <= y && y < n;
    }
}

 

주의 )

  처음 ans의 값을 100으로 설정해서 틀렸다.

  초기에 주어진 바이러스가 1개이고 가로세로 50의 ㄹ모양의 연구소가 주어지면 100초 이상 걸리기 때문에 넉넉한 값을 주어야 한다.

반응형
반응형

문제 링크

 

13549번: 숨바꼭질 3

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

 

 

풀이 ) BFS

  방문한 요소에 대한 재방문을 방지하기 위해 boolean 배열을 선언해줬다.

  같은 depth에 있는 요소를 방문할 때 2배할때는 depth가 더 깊어지지 않기 때문에 queue의 앞에 삽입해주기 위해 deque를 사용했다.

  size만큼의 요소를 방문하면 depth가 늘어나기 때문에 앞에 삽입한 이후 size를 늘려준다.

 

  나중에 찾아보니 이런 방식으로 0-1BFS라고 한다고 한다.

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] in = br.readLine().split(" ");

        int n = Integer.parseInt(in[0]), k = Integer.parseInt(in[1]);
        boolean[] visited = new boolean[100001];
        if(k <= n){
            System.out.println(n-k);
            return;
        }

        Deque<Integer> deq = new ArrayDeque<>();
        deq.offer(n);
        int ans = -1;
        while(!deq.isEmpty()){
            ans++;
            int size = deq.size();
            while(size-- > 0){
                int cur = deq.poll();
                if(cur == k){
                    System.out.println(ans);
                    return;
                }
                visited[cur] = true;

                int tmp = cur*2;
                if(tmp <= 100000 && !visited[tmp]){
                    deq.offerFirst(tmp);
                    size++;
                }
                tmp = cur+1;
                if(tmp <= k && !visited[tmp])
                    deq.offerLast(tmp);

                tmp = cur-1;
                if(0 <= tmp && tmp <= 100000 && !visited[tmp])
                    deq.offerLast(tmp);
            }
        }
    }
}

 

주의 )

  처음에 위치를 x에서 x-1로 이동함에 있어서 k보다 큰 경우만 가능하도록 했지만 예제와 같이

  5 - 10 - 9 - 18 - 17에서 10 - 9와 같이 목표인 17보다 작아도 내려가는 경우가 있어서 다시 처리해줬다.

반응형
반응형

문제 링크

 

1963번: 소수 경로

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

www.acmicpc.net

 

 

풀이 )

  현재 입력받은 수에 대해서 한 자리씩 숫자를 하나씩 바꿔가면서 BFS를 한다.

  같은 수를 다시 Queue에 넣는 것을 방지하기 위해 visited 배열을 사용하여 방문 여부를 확인하여 방문한 경우 Queue에 넣지 않는다.

 

  한 자리씩 바꾸는 경우 현재 수 num, 자릿수 digit, 바꾸자하는 수 i를 매개변수로 받아 결과값을 반환하는 make( )를 만들었다. (제곱을 구하는 pow( )에서는 밑을 10으로 고정하여 지수만 입력받아 반환하도록 했다.)

  ex ) 1234에서 10^2의 자리수 2를 7로 바꾼다하면 make(1234, 2, 7)을 호출한다.

         1734바꾸면 되므로

  • 10^2보다 큰 자리에 대해선 그대로 두기 위해 num/pow(digit+1)*pow(digit+1)
  • 10^2자리에서는 pow(digit)*i
  • 10^2이하 자리에서는 num%pow(digit);

 

#include <iostream>
#include <queue>
#include <utility>

using namespace std;

bool prime[10000];

int pow(int num){
    if(!num)
        return 1;
    return 10*pow(num-1);
}

int make(int num, int digit, int i){
    return (num/pow(digit+1)*10+i)*pow(digit)+num%pow(digit);
}

int main(void){

    for(int i = 1000; i < 10000; ++i){
        prime[i] = true;
        for(int j = 2; j < 100; ++j){
            if(i%j==0){
                prime[i] = false;
                break;
            }
        }
    }

    int t;
    cin >> t;
    int a, b;
    while(t--){
        cin >> a >> b;

        queue<pair<int, int> > q;   // 숫자, 단계
        bool visited[10000] = {false, };
        q.push({a, 0});
        visited[a] = true;
        while(1){
            int tmp = q.front().first;
            int cnt = q.front().second;
            if(tmp == b){
                cout << cnt << '\n';
                break;
            }
            visited[tmp] = true;
            q.pop();

            for(int digit = 0; digit < 4; ++digit){
                for(int i = 0; i < 10; ++i){
                    if(digit == 3 && i == 0)
                        continue;
                    a = make(tmp, digit, i);
                    if(visited[a] || !prime[a])
                        continue;
                    q.push({a, cnt+1});
                }
            }
        }
    }
}
반응형

'문제풀이 > 백준' 카테고리의 다른 글

[JAVA] 백준 - 5052 : 전화번호 목록  (0) 2021.02.13
[C++] 백준 - 1744 : 수 묶기  (0) 2021.01.26
[C++] 백준 - 1744 : 수 묶기  (0) 2021.01.15
[C++] 백준 - 1068 : 트리  (0) 2021.01.13
[C++] 백준 - 2580 : 스도쿠  (0) 2021.01.13
반응형

문제 링크

 

17471번: 게리맨더링

선거구를 [1, 4], [2, 3, 5, 6]으로 나누면 각 선거구의 인구는 9, 8이 된다. 인구 차이는 1이고, 이 값보다 더 작은 값으로 선거구를 나눌 수는 없다.

www.acmicpc.net

 

 

풀이 )

  1번 도시를 포함한 선거구 v1, 1번 도시를 포함하지 않은 선거구 v2로 나누었다. 이때 나눌때는 완전탐색을 했다.

  이후 나눈 두 선거구가 가능한지 여부를 BFS를 사용해서 확인해서 가능한 경우, 두 선거구의 인원수 차이를 갱신한다.

 

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

int n;
int total_pop = 0;
int popul[10];
bool adj[10][10];
vector<int> v1;
vector<int> v2;
vector<int> tmp1(1);	// v1 초기화용
vector<int> tmp2;		// v2 초기화용
int ans = 1e9;

bool check(vector<int> v){
    int len = v.size();
    if(len == 1)		// 도시가 1개일 경우 반드시 가능
        return true;

    vector<bool> vb(len);
    queue<int> q;
    q.push(v[0]);
    vb[0] = true;

    while(!q.empty()){		// BFS로 선거구 도시 방문
        int cur = q.front();
        q.pop();
        for(int i = 1; i < len; ++i){
            if(!vb[i] && adj[cur][v[i]]){
                q.push(v[i]);
                vb[i] = true;
            }
        }
    }

    for(int i = 0; i < len; ++i)
        if(!vb[i])
            return false;
    return true;	// 모든 도시 방문 했는지
}

void divide(int pos, int cnt, int target){
    if(cnt == target){
        v2 = tmp2;
        int len = v1.size();
        int city = 1;
        int cur = 1;
        while(1){		// v1 선거구를 이용해 나머지 도시를 v2 선거구로
            if(city == n)
                break;
            if(cur == len || city < v1[cur])
                v2.push_back(city);
            else
                cur++;
            city++;
        }
        if(check(v1) && check(v2)){		// 두 선거구 가능
            int len = v1.size();
            int v1Sum = 0;
            for(int j = 0; j < len; ++j)
                v1Sum += popul[v1[j]];

            if(abs(total_pop-2*v1Sum) >= ans)
                return;
            ans = abs(total_pop-2*v1Sum);
        }
        return;
    }

    for(int i = pos; i < n; ++i){		// 완전탐색으로 v1 선거구 도시 선택
        v1.push_back(i);
        divide(i+1, cnt+1, target);
        v1.pop_back();
    }
}

int main(void){
    cin >> n;
    for(int i = 0; i < n; ++i){
        cin >> popul[i];
        total_pop += popul[i];
    }

    for(int i = 0; i < n; ++i){
        int num;
        cin >> num;
        while(num--){
            int city;
            cin >> city;
            adj[i][city-1] = true;
            adj[city-1][i] = true;
        }
    }

    for(int i = 1; i < n; ++i){
        v1 = tmp1;
        v2 = tmp2;
        divide(1, 1, i);
    }

    if(ans == 1e9)
        ans = -1;
    cout << ans << '\n';
    return 0;
}
반응형

'문제풀이 > 백준' 카테고리의 다른 글

[C++] 백준 - 15686 : 치킨 배달  (0) 2021.01.06
[C++] 백준 - 1107 : 리모컨  (0) 2020.12.28
[C++] 백준 - 14503 : 로봇 청소기  (0) 2020.12.23
[C++] 백준 - 1719 : 택배  (0) 2020.12.22
[C++] 백준 - 15684 : 사다리 조작  (0) 2020.12.21
반응형

문제 링크

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크

www.acmicpc.net

 

풀이 )

  먼저 완전탐색으로 3개의 벽을 세운다.

  벽을 세운 모든 경우에 대해서 바이러스가 퍼진 지도를 BFS로 구한 뒤 바이러스가 퍼지지 않은 좌표의 수의 최댓값을 구한다.

 

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>

using namespace std;

int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
int n, m, ans;
int map[8][8];
queue<int> q;

int spread(void){
    int ret = 0;
    int tmp[8][8];
    memcpy(tmp, map, sizeof(map));
    queue<int> tmpq(q);

    while(!tmpq.empty()){
        int x = tmpq.front()/10;
        int y = tmpq.front()%10;
        tmpq.pop();
        for(int dir = 0; dir < 4; ++dir){
            int xx = x+dx[dir];
            int yy = y+dy[dir];
            if(xx < 0 || xx >= n || yy < 0 || yy >= m)
                continue;
            if(tmp[xx][yy] > 0)
                continue;
            tmp[xx][yy] = 2;
            tmpq.push(xx*10+yy);
        }
    }

    for(int i = 0; i < n; ++i)
        for(int j = 0; j < m; ++j)
            if(tmp[i][j] == 0)
                ret++;

    return ret;
}

void wall(int x, int y, int cnt){
    if(cnt == 3){
        ans = max(ans, spread());
        return;
    }

    for(int i = 0; i < n; ++i){
        for(int j = 0; j < m; ++j){
            if(map[i][j] == 0){
                map[i][j] = 1;
                wall(i, j, cnt+1);
                map[i][j] = 0;
            }
        }
    }
}

int main(void){
    cin >> n >> m;
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < m; ++j){
            cin >> map[i][j];
            if(map[i][j] == 2)
                q.push(i*10+j);
        }
    }

    wall(0, 0, 0);
    cout << ans << '\n';
}

 

주의 )

  바이러스가 퍼질 때, 지도와 바이러스의 위치를 담은 queue를 각각 복사해서 사용해야 다음번 시도에 영향을 주지 않는다.

반응형

'문제풀이 > 백준' 카테고리의 다른 글

[C++] 백준 - 1748 : 수 이어 쓰기 1  (0) 2020.12.12
[C++] 백준 - 5373 : 큐빙  (0) 2020.12.09
[C++] 백준 - 9663 : N-Queen  (0) 2020.12.09
[C++] 백준 - 1065 : 한수  (0) 2020.12.07
[C++] 백준 - 17144 : 미세먼지 안녕!  (0) 2020.12.07

+ Recent posts